Skip to content

feat: host the Network Descriptor and JWKS (network-adapter#74) - #79

Closed
tdprajwal wants to merge 5 commits into
NetworkofNetworkfrom
feat/74-federation-hostdescriptotJWKS
Closed

tdprajwal wants to merge 5 commits into
NetworkofNetworkfrom
feat/74-federation-hostdescriptotJWKS

Conversation

@tdprajwal

@tdprajwal tdprajwal commented Oct 12, 2026 •

Copy link
Copy Markdown

Resolves OpenAgriNet/network-adapter#74 (parent OpenAgriNet/network-adapter#48).

What

networkofnetwork/ stands up two OAN networks on one machine. For #74, make host-descriptor (bin/oanfed.py publish) builds each network's Network Descriptor from its registry entry, signs it, and writes it together with its JWKS. nginx serves both as static files at the fixed paths:

  • GET https://{networkId}/.well-known/openagrinet
  • GET https://{networkId}/.well-known/jwks.json

The open decision from #74

  • Signature algorithm: Ed25519. That is its fully-specified JOSE name (RFC 9864, which deprecates EdDSA).
  • Canonical encoding: RFC 8785 (JCS) over the descriptor without signature. Keys are ordered by UTF-16 code units. Floats, integers above 2^53 and lone surrogates are refused, so every implementation rebuilds identical bytes.

Keys (matches Trust and signatures / Trust and key lifecycle in the doc)

Key Where Signs Published
Governance keys/governance.key, generated mode 600, never in the registry The Descriptor only JWKS; signature.keyId
Operational The registry entry's active, unexpired Ed25519 keys (the adapters' traffic keys) Traffic JWKS; delegated by kid in the signed operationalKeys
  • kid = RFC 7638 thumbprint, so a rotated key gets a new kid without any configuration. The overlap comes from the registry entry, which already holds keys as a list with validUntil / status.
  • Peers' admit stores only the delegated operational keys. The governance key can never pass a traffic check, and a key slipped into the JWKS without being delegated in the descriptor is ignored.

Validation (before signing, and on verify)

The descriptor is checked against the contract table:

  • version is 1.0; networkId is a lowercase DNS domain.
  • name, operator.name and status (Active/Suspended/Retired) are present; revision is an integer ≥ 1.
  • issuedAt is UTC and not in the future (5 min skew allowed); operatingProfile is non-empty.
  • schemaPacks and services.discovery are https URLs; contacts and projectionTtl are well-formed.

The JWKS is checked too: no duplicate kids, Ed25519 keys are 32 bytes, and alg/use are correct. The signer must not also be delegated for traffic, and every delegated kid must be published.

publish refuses to write when:

  • the revision does not increase over what is already published (a corrupt published file is an error, not revision 0);
  • the governance key is also a registered traffic key;
  • the key file is readable by group or others;
  • there is no usable traffic key;
  • the document fails its own verification.

It writes the JWKS before the descriptor, each atomically, so a peer never sees a descriptor whose key is not yet published.

Proof

  • python3 -m unittest networkofnetwork/bin/test_oanfed.py: 21 tests pass. They cover the JCS RFC 8785 vectors, tampering with every signed field, malformed signatures and JWKS, contract fields, revision rollback, key-file permissions, the key split, delegation-only admit, and revoked/expired/upcoming keys.
  • Ran publish against a live registry: the output verifies.

Out of scope (sibling issues)

Secrets

networks/ (per-network keys, TLS, .env) is git-ignored. .env.example holds only local demo defaults.

tdprajwal and others added 5 commits October 12, 2026 17:13
…penAgriNet/network-adapter#74]

Adds networkofnetwork/: two OAN networks on one machine, federated.
oanfed.py publish builds each network's Network Descriptor from its
registry entry, signs it, and writes it with the JWKS that verifies it.
nginx serves both as static files at the fixed paths
/.well-known/openagrinet and /.well-known/jwks.json.

Signature: Ed25519. Canonical form: RFC 8785 (JCS), covering every field
except signature. Generated per-network material (keys, TLS, .env) stays
out of git via networkofnetwork/.gitignore.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… descriptor [OpenAgriNet/network-adapter#74]

- Governance key (keys/governance.key, mode 600) signs only the Network
  Descriptor. Operational keys come from the registry entry and are
  delegated by kid in the signed operationalKeys field; the JWKS
  publishes both. admit stores only delegated keys, so the governance
  key can never verify traffic.
- canonical() is RFC 8785: UTF-16 key order; floats, unsafe integers and
  lone surrogates are refused. Signature algorithm Ed25519 (RFC 9864).
- kid is the RFC 7638 thumbprint, so rotation needs no configuration.
- Descriptor and JWKS validated against the contract before signing and
  on verify. publish refuses a non-increasing revision, a governance key
  that is also a traffic key, or a readable key file. It writes the
  JWKS before the descriptor, each atomically.
- test_oanfed.py: 21 checks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…/network-adapter#74]

make e2e-descriptor publishes a new revision, fetches it over TLS through
the front door as the peer would, tampers with the served files to confirm
each forgery is refused, then restores what was served. Nothing is written
to either registry.
…network-adapter#74]

Adds an "Identity documents" folder: GET each network's
/.well-known/openagrinet and /.well-known/jwks.json from its front door
and check the Descriptor contract fields, the Ed25519 keys, and that the
governance key is published but never delegated for traffic.
network.sh runs under set -e, so reading a missing .env exited make with
no message. Check for the network's state first and name the fix.
@tdprajwal

Copy link
Copy Markdown
Author

Closing; a new PR will follow.

@tdprajwal tdprajwal closed this Oct 13, 2026
@tdprajwal
tdprajwal deleted the feat/74-federation-hostdescriptotJWKS branch October 13, 2026 06:21
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