Files
zap-spec/capabilities.zap
zeekay 9198e3b89b capabilities: bump sig field 96 → 3408 for ML-DSA-65 (v1.1)
Widen the Sig footer from sig96 (96 B) to sig3408 (3408 B) so a full
FIPS 204 ML-DSA-65 signature (3309 B) fits inline. Capability struct
grows from 260 B to 3572 B; Revocation from 136 B to 3448 B. The field
offset of Sig itself (164) is unchanged — only the field width.

3408 is the smallest 16-byte-aligned size that fits ML-DSA-65 with
99 B headroom. Schemes shorter than 3408 (secp256k1 65 B, Ed25519
64 B) are zero-padded on the right; the algorithm tag at Sig[3407]
tells the verifier which primitive to dispatch on.

Wire-incompatible with v1.0. Per the project's "no backwards
compatibility, only forwards perfection" rule, every encoder/decoder
updates in lockstep at the v1.1 tag — no compat layer, no union, no
version negotiation.

Cross-references:
  - capabilities_kinds.md gains the "Wire schemes" tag table.
  - SPEC.md §6 documents the v1.0 → v1.1 wire transition.
2026-06-07 11:45:13 -07:00

105 lines
5.5 KiB
Plaintext

# ZAP capability schema v1.1
#
# Object-capability auth across the Lux/Liquidity stack. Replaces bearer JWTs:
# every authority is a typed, transferable, attenuable, revocable Capability
# signed by the issuer and held by a named principal. Capnp-level-4 sturdy refs
# in spirit, zcap-ld in compatibility.
#
# Cross-references:
# - capabilities_kinds.md - CapKind, CaveatKind, Permission bit assignments
# - SPEC.md - Issue/Attenuate/Verify/Revoke semantics, threat model
#
# Wire stability: this is the v1.1 wire format. v1.1 widens the signature
# footer from 96 bytes (v1.0) to 3408 bytes so a full ML-DSA-65 signature
# (FIPS 204 §5.2, 3309 bytes) fits inline. v1.0 buffers do NOT round-trip
# through v1.1 parsers and vice versa - the field-width change shifts the
# struct size from 260 bytes to 3572 bytes. There is no compat layer; per
# the project's "no backwards compatibility, only forwards perfection"
# rule, every encoder/decoder is updated in lockstep at the v1.1 tag.
#
# Future shape changes still require a new struct name + new ShapeKind.
# All offsets are in bytes from the start of the fixed header.
package cap
# 32-byte object identity hash (BLAKE3 over canonical bytes). Used for object
# targets, principal pubkey hashes, parent-cap hashes, asset IDs, etc.
type id32 = bytes_fixed[32]
# 3408-byte signature footer. Sized to fit any of:
# - secp256k1 ECDSA (65 bytes, padded right with zeros)
# - Ed25519 (64 bytes, padded right with zeros)
# - ML-DSA-65 (3309 bytes per FIPS 204 §5.2, padded right with zeros)
# - hybrid Ed25519+ML-DSA-65 (3309 + 64 + 16-byte separator, padded)
# 3408 is 16-byte aligned (3408 = 213 * 16) for cache-friendly access and
# leaves 99 bytes of headroom above the FIPS 204 figure.
#
# Algorithm dispatch: encoders write the algorithm tag in the final byte
# of the footer (0x01 secp256k1 ECDSA, 0x02 Ed25519, 0x03 ML-DSA-65, 0x04
# hybrid Ed25519+ML-DSA-65). Decoders read the tag byte to know which
# verifier to invoke; the tag byte is INSIDE the signed payload (the
# signer sets it before computing the signature) so a tag flip changes
# the signature and is detected by verifier mismatch.
type sig3408 = bytes_fixed[3408]
# Capability - a typed authority over a target object.
#
# Replaces bearer JWTs across the stack. Issued by a principal, held by another
# principal, exercised against an object. Layout is at v1.1.
#
# Signature scope: bytes [0..164) of this struct ('s fixed header up to and
# including Caveats list pointer), concatenated with the canonical encoding
# of every Caveat in Caveats (kind-byte then value bytes, in list order).
# See SPEC.md "Signature chain" for the exact rule.
struct Capability {
Kind u32 @0 # CapKind: authority profile (see capabilities_kinds.md)
Target id32 @4 # 32B BLAKE3 hash of the object this cap grants over
Holder id32 @36 # 32B hash of the wielder's public key (binds to keypair, not bearer)
Issuer id32 @68 # 32B hash of the issuer's public key (verifier resolves to pubkey via registry)
Permissions u64 @100 # bitmask: which ops on Target are permitted (must subset of Parent's)
Parent id32 @108 # hash of parent Capability bytes (all-zero = root cap; revoke parent -> chain dies)
IssuedAt u64 @140 # unix seconds, issuer's clock at signing time
ExpiresAt u64 @148 # unix seconds, 0 = never (caveat CaveatKindExpiresAt may tighten further)
Caveats list<Caveat> @156 # macaroon-style restrictions, AND-composed; each child cap MAY add caveats
Sig sig3408 @164 # issuer signature over Capability[0..164) || Caveats canonical bytes
}
# Caveat - composable restriction on a Capability.
#
# Caveats are AND-composed: every caveat in the leaf cap AND every caveat
# in every ancestor must be satisfied for the cap to authorize an action.
# A child cap MAY add caveats but MUST NOT remove or weaken parent caveats.
struct Caveat {
Kind u32 @0 # CaveatKind: the restriction class (see capabilities_kinds.md)
Value bytes @4 # kind-specific binary payload (length defined per-kind in capabilities_kinds.md)
}
# Revocation - kill-entry for a Capability or its descendants.
#
# Published on-chain (LP-XXX revocation log) or fetched from IAM's revocation
# endpoint. Listing a CapID kills that capability AND every descendant whose
# Parent chain transitively references it. Only the original Issuer may
# revoke - the RevokerSig must verify against the Issuer pubkey of the
# capability identified by CapID.
struct Revocation {
CapID id32 @0 # 32B BLAKE3 hash of the revoked Capability's canonical bytes
RevokedAt u64 @32 # unix seconds when the revocation was issued
RevokerSig sig3408 @40 # signature by the original Issuer over CapID || RevokedAt
}
# CapProof - presentation bundle for verification without network fetch.
#
# The wielder presents (Leaf, Chain) to the verifier. Chain holds the parent
# capabilities from the leaf's Parent up to the root (root has Parent=zero).
# Verifier walks Chain, checks each link's signature, each child's Issuer ==
# parent's Holder, the Permissions subset rule, the Caveat-add-only rule, and
# consults the revocation list at each step. See SPEC.md "Verify".
struct CapProof {
Leaf Capability @0 # the cap being exercised (4B nested-struct pointer)
Chain list<Capability> @4 # parents from Leaf.Parent up to root, in order
}