Upgrade Guide: 4.0.0 to 5.0.0
This guide covers the consumer-visible changes when upgrading the stable
AsiBackbone package family from 4.0.0 to 5.0.0.
5.0.0 is a security release. Every behavior change in it makes a governance
path fail closed where it previously returned a permissive result. A host whose
artifacts are produced and verified through the shipped factories, and whose
validation call sites already state what they are validating against, should
need only the package reference update and a rebuild. The remaining sections
describe the call sites that now require a decision.
Update package references
Update every consumed AsiBackbone.* package together:
<PackageReference Include="AsiBackbone.Core" Version="5.0.0" />
<PackageReference Include="AsiBackbone.AspNetCore" Version="5.0.0" />
<PackageReference Include="AsiBackbone.EntityFrameworkCore" Version="5.0.0" />
Package IDs and namespaces have not changed. Rebuild the host because the
assembly identity advances from 4.0.0.0 to 5.0.0.0.
Migrate persisted enum values
VerificationPolicyAction, SignatureVerificationCategory, GrantUseState,
and AuditIntegrityVerificationCategory now reserve zero as an Unspecified
sentinel that is rejected wherever the value is consumed. The members that
previously held zero were renumbered:
| Enum | Member | 4.x value |
5.0.0 value |
|---|---|---|---|
VerificationPolicyAction |
Allow |
0 |
7 |
SignatureVerificationCategory |
Valid |
0 |
11 |
GrantUseState |
Accepted |
0 |
5 |
AuditIntegrityVerificationCategory |
Valid |
0 |
12 |
This is the change most likely to need work. A default-constructed value, or a database column that yields zero for input it does not recognize, previously meant "allow", "valid", or "accepted". It now means "unspecified" and is refused.
If you persist any of these enums as integers — which EF Core does by
default — existing rows holding 0 will read back as Unspecified after the
upgrade. Migrate those rows before deploying:
UPDATE VerificationOutcomes SET Action = 7 WHERE Action = 0;
UPDATE VerificationOutcomes SET Category = 11 WHERE Category = 0;
Adjust table and column names to your schema. If you persist these values as strings, no migration is needed.
If you compiled against 4.x, rebuild. Enum constants are inlined at
compile time, so an assembly built against the old numbering keeps the old
integers and will disagree with this release silently rather than failing to
load.
State what capability validation is validating against
CapabilityGrantValidator.ValidateAsync no longer accepts omitted options. The
omitted-options path previously built permissive defaults — no proof, no use
check, and no issuer, audience, or scope expectations — and returned Valid for
any grant that had not expired. It now denies with
capability.validation-options-required.
Supply options describing what the grant must satisfy:
CapabilityGrantValidationResult result = await CapabilityGrantValidator.ValidateAsync(
signedGrant,
CapabilityGrantValidationOptions.CreateExecutionBoundary(
issuer: "https://issuer.example",
audience: "gateway-1",
scopes: ["robotics.execute"]),
verificationService,
useStore,
cancellationToken);
Requiring proof or a bounded-use check without an audience expectation is also refused, because proof establishes that a grant was signed rather than that it was issued for this audience.
Anchor partial audit chain verification
AuditIntegrityVerifier.Verify with requireGenesis: false now requires the
hash of the link preceding the supplied range:
AuditIntegrityVerificationResult result = AuditIntegrityVerifier.Verify(
links,
expectedChainId: chainId,
requireGenesis: false,
expectedPreviousLinkHash: precedingLink.LinkHash);
Without an anchor the call fails with
integrity.expected-previous-hash-required. Previously the verifier compared
the first supplied link against an empty previous hash, which rejected genuine
partial chains and accepted rewritten ones.
To establish that you hold a complete chain rather than a valid prefix, supply
expectedTipLinkHash or expectedTipSequence. A mismatch reports
TruncatedChain.
Review verification policy overrides
Mapping a failure category to VerificationPolicyAction.Allow now requires
allowUnsafeAllowOverrides: true on VerificationPolicyOptions.Create. A
key-pin mismatch now reports signature.key-not-trusted under the new
UntrustedKey category, which denies by default rather than escalating, and a
stripped signature denies when proof is required rather than mapping to
RequireAcknowledgment.
If your host branched on UnknownKeyVersion for pin mismatches, branch on
UntrustedKey instead.
Expect signing to throw when no signature is produced
GovernanceArtifactSigner.Sign*Async now throws when the provider returns no
signature. To accept and inspect an unsigned result, opt in explicitly:
SignedGovernanceArtifact<AuditLedgerRecord> artifact =
await GovernanceArtifactSigner.SignAuditLedgerRecordAsync(
record,
signingService,
requireSignature: false,
cancellationToken: cancellationToken);
Replace local-development signing outside development
UseLocalDevelopmentSigning throws when the environment is Production. The
provider generates its key per process and never persists it, so artifacts it
signs stop verifying after a restart.
Register a managed key provider for production, or state the intent explicitly when a non-production workload is deliberately using an ephemeral key:
builder.UseLocalDevelopmentSigning(
LocalDevelopmentSigningOptions.Create(allowInProduction: true));
The guard reads DOTNET_ENVIRONMENT and then ASPNETCORE_ENVIRONMENT. A host
that determines its environment another way can pass environmentName.
The new ASIB003 analyzer diagnostic reports registrations that sit behind no
environment check at all. Guard the call, set AllowInProduction, or apply the
existing host review marker to accept it deliberately.
Add email claims back if you want them
ClaimTypes.Email is no longer a default actor-id or display-name claim source,
because actor identifiers and display names are persisted verbatim and indexed
in durable audit rows. If your deployment intends to record email addresses
there, add the claim types back explicitly and accept the retention
consequences:
services.Configure<AsiBackboneHttpActorContextOptions>(options =>
{
options.ActorIdClaimTypes.Add(ClaimTypes.Email);
options.DisplayNameClaimTypes.Add(ClaimTypes.Email);
});
Bind capability use limits at issuance
A grant can now carry the use limit its issuer authorized. A limit supplied only at the validation call site is local policy that nothing in the signature covers; validation uses the narrower of the two.
To bind one, create the grant with a limit and record the schema version that carries it:
CapabilityTokenGrant grant = CapabilityTokenGrant.Create(
tokenId: tokenId,
issuer: issuer,
audience: audience,
scopes: scopes,
issuedUtc: issuedUtc,
expiresUtc: expiresUtc,
maxUseCount: 1,
schemaVersion: AsiBackboneSchemaVersions.StableArtifactsV2);
This is opt-in. A grant recording StableArtifactsV1 canonicalizes exactly as
it did in 4.x, so grants signed before this release keep verifying unchanged.
Normalize canonical payload metadata keys
CanonicalPayloadBuilder now throws when metadata keys collide after trimming,
because "k" and " k" both normalize to "k" and the surviving value
depended on enumeration order. Normalize metadata keys before signing.
Validate the upgrade
- Rebuild every host assembly against
5.0.0.0. - Migrate persisted enum columns that hold zero.
- Run the host's denial-path tests and confirm blocked decisions still stop before the executor.
- Re-verify a sample of retained signed artifacts and audit chains, supplying an anchor for partial chains.
- Confirm no production configuration path registers local-development signing.