# Runtime delegation v1

Runtime delegation v1 is the neutral, signed artifact passed from an L1
workload to Runtime after Runtime Identity has intersected the authoritative
user, workload, installation, and capability grants. This document specifies
the shared semantic and cryptographic grammar only. It does not implement an
issuer service, Runtime admission, persistence, or production routing.

## Ownership

The signed semantic claims are exactly:

```text
DelegationID
OrgID
PrincipalID
SessionID
CallerWorkloadID
AppInstallationID
RuntimeAudience
Capabilities[]
AuthEpoch
SecurityEpoch
IssuedAt
ExpiresAt
```

`SigningKeyID` is authenticated protected-envelope metadata, not a business
claim. OAuth scopes and session-class mappings are issuer inputs, never a
second signed grant set. Empty or unknown mappings contribute an empty grant
set, so `IntersectCapabilities` returns no Runtime capability. Its fixed
`CapabilityGrantSets` requires workload, AppInstallation, session/scope, and
requested axes; callers cannot silently omit one intersection. The artifact
contains no bearer token, permission/FLS/RLS map, profile or role, record data,
database value, or caller-selected security context.

Workload, AppInstallation, and mapped session grants are independently trusted
authority sets, not the capabilities carried by one request or signed token.
Each authority set may contain up to 1,024 sorted, unique capabilities;
`ValidateCapabilityGrants` validates that separate bound and permits an empty
set (which grants nothing). The verifier's trusted supported-capability registry
and a Catalog serving binding's workload capability ceiling use the same 1,024
bound; those two configurations must remain nonempty. All entries retain the
128-byte capability grammar. Every complete authority set is validated, including
entries outside the requested intersection.

Requested capabilities remain bounded at 32, and issuance still requires each
result to appear in **all four** axes. Consequently a broad application grant
can authorize a one-capability request without producing a broader token. These
trusted-configuration bounds do not change the signed v1 grammar or permit
deployments to relax its wire ceilings.

The independent authoritative axes remain outside this codec:

- `AuthEpoch` is keyed by `(OrgID, SessionID)`.
- `SecurityEpoch` is keyed by `OrgID`.
- `SecurityLease` is keyed by
  `(OrgID, RuntimeReplicaID, SecurityEpoch, ExpiresAt)`.
- delegation revocation is keyed by `(OrgID, DelegationID)`.

P6c supplies authenticated workload and expected binding facts to `Verify`,
then performs admission. P6d owns the stores, revocation, installation
projection, epochs, and leases. Neither axis is inferred by this package.

`Verify` remains the one-step helper when the accepting owner already knows one
exact Org/AppInstallation binding. A multi-installation workload cannot know
those selectors from transport identity alone. For that case,
`VerifyCallerBound` first authenticates the complete signed claim set and binds
the transport-known workload, audience, time window, and required capability.
The accepting Runtime must then validate the returned signed Org,
AppInstallation, revocation, and epoch facts through its authoritative security
state before creating trusted request context. Caller-bound verification is not
installation authorization and must never reach a data handler by itself.

## Cryptographic and wire grammar

The semantic package is
`github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/delegationv1` and remains
Go-stdlib-only. The v1 artifact is
[RFC 7515](https://www.rfc-editor.org/rfc/rfc7515) compact JWS with an
[RFC 8037](https://www.rfc-editor.org/rfc/rfc8037) Ed25519 signature. Callers
cannot select another algorithm. Its closed, explicitly typed validation
profile follows the cross-JWT guidance in
[RFC 8725](https://www.rfc-editor.org/rfc/rfc8725).

The protected header is closed and canonical:

```json
{"alg":"EdDSA","kid":"…","typ":"nexuscrm-runtime-delegation+jwt","nexuscrm_version":1,"nexuscrm_domain":"nexuscrm.runtime.delegation.v1"}
```

`typ`, `nexuscrm_version`, and `nexuscrm_domain` are authenticated domain
separators. Together with the closed claims grammar and audience validation,
they provide mutually exclusive validation rules from public user JWTs and
other future signed artifacts. Unknown header/claim members, alternate member
order or whitespace, duplicate members, padded/non-canonical base64url, another
algorithm, domain, or version all fail closed.

The payload is UTF-8 JSON in the field order frozen by the immutable vector at
`pkg/runtime/delegationv1/testdata/ed25519-jws-v1.json`. Capabilities use
ascending bytewise order and must be unique. The only transport carrier is the
ASCII gRPC metadata value named by
`wire-go/envelope.RuntimeDelegationMetadataKey`:
`x-nexuscrm-runtime-delegation`. It is not a protobuf request field and does not
change `RecordsService.ReadSelectedRoot`.

Verification is ordered as follows:

```text
outer byte bound and compact syntax
→ canonical protected header and candidate key selection
→ current/overlap-key Ed25519 verification
→ canonical claims parse and structural bounds
→ iat/exp and configured skew
→ audience
→ expected workload/org/AppInstallation/RPC capability
→ return authenticated Claims
```

For `VerifyCallerBound`, the final expected Org/AppInstallation comparison is
replaced by the mandatory authoritative Runtime security-state lookup described
above. This avoids both unsafe parsing before signature verification and the
false assumption that one authenticated workload maps to only one tenant or
installation. Signature, time, audience, workload, supported-capability, and
required-capability checks are identical in both paths.

Nothing returns semantic claims before signature verification. A key ID is
untrusted during lookup and becomes authenticated only when its protected
header verifies. An immutable `Verifier` clones its public-key snapshot and is
safe for concurrent use; rotation publishes a replacement snapshot. Current
and overlap keys verify, while retired and unknown keys fail closed. A usable
snapshot has exactly one current key; zero or multiple current keys are a
fail-closed configuration error.

Malformed, non-canonical, bad-signature, unknown/retired-key, not-yet-valid,
expired, and wrong-audience artifacts return disclosure-safe
`FailureUnauthenticated`. A valid artifact substituted across workload, org,
AppInstallation, or required capability returns disclosure-safe
`FailureForbidden`. Both have identical generic error text; P6c may map the
category but must not expose existence details.

`IssuedAt` is the earliest acceptance instant for v1; there is no additional
`NotBefore` claim because P6a froze the exact claim inventory. `ExpiresAt` is
exclusive. A verifier may allow whole-second clock skew only up to the hard
ceiling below.

## Hard protocol ceilings

These are security/complexity ceilings of the v1 grammar, not observed product
values and not approval of production policy:

| Fact | Hard v1 ceiling |
|---|---:|
| Opaque identifier | 255 UTF-8 bytes |
| Caller workload identifier | 1,024 UTF-8 bytes |
| Signing key identifier | 128 ASCII bytes |
| Requested or signed-token capabilities | 32 |
| One capability | 128 ASCII bytes |
| Compact JWS metadata value | 16 KiB |
| Delegation lifetime (`exp - iat`) | 10 minutes |
| Verifier clock-skew allowance | 30 seconds |

The identifier bounds prevent attacker-controlled signed and pre-verification
lookup inputs from becoming unbounded. The separate authority and request bounds
limit set validation/intersection; the 32-capability signed-token ceiling is
unchanged. The 16 KiB outer ceiling contains the complete
maximum semantic shape: the exact all-maxima test vector, including worst-case
JSON string escaping, maximum integer widths, protected header, and Ed25519
signature, is 13,168 bytes. The ceiling is checked before decoding or key
lookup. The lifetime and skew ceilings bound the maximum replay window
permitted by this protocol.

P6-D must approve shorter production values for issuance lifetime, configured
clock skew, key overlap, and security staleness. A deployment may only tighten
these ceilings. P6b does not approve a production lifetime or SLO.

## Compatibility

The initial vector freezes the exact header, payload, signing input, Ed25519
public test key, signature, and compact serialization. It is an immutable N
baseline. There is no prior delegation wire version to exercise as N-1; a
future incompatible grammar must introduce a new envelope version and retain
v1 vectors during its documented compatibility window.

The root semantic module and the separately versioned `wire-go` module require
additive releases. From the current `v0.1.3` / `wire-go/v0.1.0` baseline, the
release plan is root `v0.1.4` and `wire-go/v0.1.1`. The root release carries
claims, signing, verification, and limits; the wire release carries the exact
metadata-key constant. Neither module adds protobuf or changes the
selected-root compatibility baseline.
