Skip to content

Feat/subscription gated terms - #57

Merged
EugeneDymo merged 5 commits into
mainfrom
feat/subscription-gated-terms
Oct 28, 2026
Merged

EugeneDymo merged 5 commits into
mainfrom
feat/subscription-gated-terms

Conversation

@EugeneDymo

Copy link
Copy Markdown
Member

No description provided.

The delegation chain is now one nested token with a fixed profile: each
child carries its complete signed parent in fora_parent and its own key in
the protected jwk header, at most four tokens and 32768 bytes, with typ
fora-delegation+jwt, alg EdDSA or Ed25519 on an Ed25519 key, required iss,
aud, cnf.jkt and exp, a 60-second clock leeway, configured root trust and
the intermediate issuer rule. The authentication page is the normative
text and the proto comments follow it.

helpers.VerifyDelegation implements the profile. It checks the Ed25519
signature with the standard library under either alg name, because the
pinned go-jose accepts only EdDSA, and keeps go-jose for the jwk header and
the thumbprint. Its failures are typed, and testdata/delegation-chains.json
carries signed chains with outcomes declared from the profile; the Go tests
re-verify every case. The TypeScript and Python verifiers are deferred and
recorded as a decision in the parity matrix.

ScopeCovers and ScopesCover implement the segment-wise scope rule in Go,
Python and TypeScript, and ScopesSubset is grant containment rather than
exact membership, all pinned to a hand-written scope-match-vectors.json.

Delegation.token carries bytes.max_len = 32768. The JSON Schema plugin
renders a bytes limit as a base64 length that counts padding, which admits
a token one byte over, so a byteslengen manifest lets the schema pipeline
write an exact base64 pattern; the corpus accepts 32768 bytes and refuses
32769.

Nine documentation pages describe the same chain, the tokens they show are
real cases from the chain file, and a conformance test verifies each one.
Both changelog files and the design history record the change.
The compact JWS parser now accepts only unpadded base64url segments, so a
CR or LF the base64 decoder would skip is a refusal; reads the payload as
exactly one JSON object, so bytes after the claims object under a valid
signature are a refusal; and refuses the b64 header parameter, with or
without crit. NumericDate claims are bounded to 9999-12-31T23:59:59Z, the
last second a protocol Timestamp carries, so a finite exp like 1e100 is a
bad claim instead of an overflowed expiry. Raw-byte cases cover each in
the unit tests and the shared chain file.

The authentication page and the denial reason comment now tell the three
outcomes apart: a token over 32768 bytes is refused at wire validation
before any item, a failed chain at discovery hides the gated terms and
reports nothing, and a failed chain at purchase denies the item.

The Exchange discovery example verifies the chain once per subscription
record with that record's own keys, so an issuer trusted for one
subscription grants nothing on another, keeps the HTTP proof separate from
the delegation result, derives the holder from the verified signing key,
and hands the per-record grants with their expiry to catalog selection.
The example is now extracted into a package that compiles against typed
stubs, which also fixed its money and quota field types.
…or refusals in the chain corpus

A zero DelegationInput.Now was converted through UnixNano, which is
undefined for the zero time and made every expiry check pass. A zero Now
now means time.Now(), like the request and PoP verifiers, the clock is
read once, and now is built from Unix() and Nanosecond().

The chain corpus gains ancestor claim and time refusals on the root under
a valid child and on the middle token of three, a five-token chain whose
fifth token is not a compact JWS (still too_deep, since depth is counted
before decoding), issuer key replacement with both keys listed and then
the old key withdrawn, and the docs_walkthrough_academic token now grants
the subscription scope. Every refusal declares the token index under the
existing DelegationError.Token contract and the replay asserts it. The
file note, the generator header and the parity decision state that
mirrors are replayed only by an Exchange's own tests.
…d term only on a direct query

The operator page, the request-flows page and walkthrough v1 restated
the verification steps and limits; they now keep page-specific facts and
link to the Verification section. The operator page names the three
failure outcomes instead of denying on any failed check, and both
DENIAL_REASON_DELEGATION_INVALID table rows follow the enum comment. The
stale revocation step is gone: revocation_uri is informational, the
lookup is not part of the profile, and the proto Delegation comment is
cut to what a field reader needs with a pointer to the page. Bindings,
the type export and both changelogs are regenerated or updated together.

The academic walkthrough contradicted itself: the subscription term is
gated, so the Broker's query cannot present it. The Broker query now
shows the paid offer, a new direct query signed by the agent with its
delegation presents the subscription offer, and the purchase consumes
that offer. The example token grants the subscription scope, and the
paper counts, totals and summary table are reconciled.
@EugeneDymo
EugeneDymo merged commit 060933f into main Oct 28, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant