Skip to content

feat: add federation fields to the Participant registry schema [#71] - #74

Closed
tdprajwal wants to merge 4 commits into
NetworkofNetworkfrom
feat/71-registry-federation-fields
Closed

tdprajwal wants to merge 4 commits into
NetworkofNetworkfrom
feat/71-registry-federation-fields

Conversation

@tdprajwal

@tdprajwal tdprajwal commented Oct 9, 2026 •

Copy link
Copy Markdown

What

Extends the registry's Participant schema with the fields federation needs,
lets role hold a list, records peer admission per direction, and fixes
PublicKey so node records can be updated.

Four commits, rebase-merge friendly:

  • feat — role accepts a single value or a list; new optional fields
    federationRole, revision, schemaPacks (https URLs) and
    projectionTtl (seconds, 0 = live query only).
  • fix — PublicKey declares the five system fields Sunbird RC stamps on
    every stored key (osid, osCreatedAt, osCreatedBy, osUpdatedAt,
    osUpdatedBy), keeping additionalProperties: false. osOwner is left out:
    the registry never sets it on a key.
  • fix — drops self from federationRole: a network recognises its own
    entry from its configuration, so a stored value is redundant.
  • feat — federationRole becomes a per-direction admission record: an
    object with optional source and requester halves, replacing the
    source/requester/both string (both halves present = both). Each half
    carries its own schemaPacks (moved from the top level),
    descriptorRevision, admittedAt and updatedAt. federationRole is
    removed from indexFields, as it is no longer a scalar.

Resulting shape, on Network A's entry for Network B:

"revision": 13,
"federationRole": {
  "source": {
    "descriptorRevision": 12,
    "admittedAt": "2026-03-01T09:00:00Z",
    "updatedAt": "2026-04-01T09:00:00Z",
    "schemaPacks": ["https://network-b.org/packs/weather"]
  },
  "requester": {
    "descriptorRevision": 12,
    "admittedAt": "2026-06-10T14:00:00Z",
    "updatedAt": "2026-08-20T11:30:00Z",
    "schemaPacks": ["https://network-a.org/packs/crop-advisory"]
  }
}

Why

Our Network Descriptor (#82, for OpenAgriNet/network-adapter#74) is built
from our own registry entry, and admitting a peer
(OpenAgriNet/network-adapter#75) writes one; the schema had nowhere to store
either.

The architecture doc stores admission independently for each direction:
"An A-to-B interaction requires Network A to admit Network B as a source and
Network B to admit Network A as a requester", and the admission record
"retains the peer networkId, Descriptor revision and digest, ... time". A
single string could not hold a different pack list, revision or time per
direction.

  • descriptorRevision is the revision a direction was admitted against,
    distinct from the top-level revision (the latest verified). A Descriptor
    update "does not silently grant or widen admission", so a top-level
    revision above descriptorRevision marks a peer due for review.
  • admittedAt is the admission time kept as evidence; each direction may be
    admitted at a different time.
  • updatedAt is when the admission decision last changed, set by us on the
    decision, unlike osUpdatedAt, which the registry stamps on any write.

Separately, every update of a node record was failing: on a PUT, Sunbird RC
re-validates the stored keys, which carry its own system fields the schema did
not allow. That would have made refreshing an admitted peer, or rotating a
key, impossible. The same applies to the nested federationRole objects, so
they declare the same system fields.

federationRole as a string never shipped on main, so replacing it needs no
data migration. Any dev deployment that ran an earlier commit of this branch
and stored a string federationRole must clear that field before updating
the record.

Testing

  • Draft-7 validator (first three commits): 15/15 cases (valid and invalid
    entries).
  • Live Sunbird RC, simulated upgrade (old schema → seed old-style rows → new
    schema → restart), first three commits:
    • old rows with a single-value role read and update; root and key osid
      unchanged
    • refused: empty/unknown/duplicate role, http schema pack, negative
      projectionTtl, revision 0
    • keys still strict: typo validUnitl, unknown key fields, unknown alg,
      and a client-supplied osOwner are refused
    • role search with contains matches both shapes
  • Draft-7 validator, per-direction federationRole:
    • accepted: both halves, source only, halves carrying registry os*
      fields
    • refused: the old string "both", an empty {}, an unknown half
      (sourse), an unknown field inside a half, a half without admittedAt,
      descriptorRevision 0
  • Live Sunbird RC v2.0.0 (fresh Postgres), per-direction federationRole,
    22/22:
    • a peer with both halves is stored exactly as sent; the registry stamps
      osid and the os* audit fields on federationRole and on each half
    • a PUT of the read-back record bumping revision is accepted; the halves
      and their osid are unchanged
    • a PUT re-admitting the source half (descriptorRevision, updatedAt)
      is accepted
    • a PUT without registry fields is accepted
    • a source-only peer, and a non-federated peer with a single-value
      role, create and update
    • refused: the old string "both", an empty {}, an unknown half, an
      unknown field in a half, a half missing admittedAt or
      descriptorRevision, descriptorRevision 0, a non-date admittedAt or
      updatedAt, an http schema pack
    • role search with contains returns only the matching peers

Notes for rollout

  • Restart sunbird-registry-service to load the schema.
  • Search role with contains, not eq, which misses a list.
  • Update a peer by PUTting the record as read back. A PUT that omits the
    halves' osid recreates them: new osid, reset osCreatedAt, and a half
    left out of the body is dropped. admittedAt survives this, which is why
    it is ours and not the registry's osCreatedAt.
  • A top-level schemaPacks is still accepted and stored, because
    Participant allows additional properties; nothing reads it. Clients
    written against the earlier commit must move it into a half.
  • Editing a schema file under quick-start/config/registry/ needs
    git add -u: the registry/ rule in .gitignore matches the folder, though
    the files are tracked.

Follow-ups (not in this PR)

  • network-adapter: the sunbirdRegistry plugin reads role as a single
    string; it must accept a list before Admit a peer network network-adapter#75 writes
    one.
  • The Helm chart's copy, charts/registry/files/schemas/Participant.json,
    is out of date; tracked in Helmcharts and automation changes for network of network #73.
  • From the architecture doc, not yet in the schema: a per-direction admission
    status (Candidate/PendingReview/Active/Suspended/Rejected/Retired) so one
    direction can be suspended alone; a top-level descriptorDigest to refuse
    same-revision-different-content; the peer's governance trust anchor; and
    moving projectionTtl into source, the only direction it applies to.

Closes #71

@tdprajwal
tdprajwal changed the base branch from main to development October 9, 2026 06:46
@tdprajwal
tdprajwal changed the base branch from development to NetworkofNetwork October 9, 2026 07:25
Federation needs the registry to record peer networks: our own entry
builds the Network Descriptor (#74) and admitting a peer writes one (#75).

- role accepts a list as well as a single value, so one participant can
  serve several layers; a single string stays valid for existing rows
- federationRole (self/source/requester/both): our admission decision,
  never read from a peer's Descriptor; added to indexFields
- revision: the Descriptor revision accepted; only increases
- schemaPacks: the peer's declared schema pack URLs, https only
- projectionTtl: seconds we may retain a peer's data; 0 means live query

All fields are optional and no existing rule is tightened, so stored
records keep their osid and stay editable.
…ields [#71]

On a PUT, Sunbird RC merges osid and the os* audit fields into each
stored key before re-validating. PublicKey did not declare them, so
every update of a node failed, including the revision bump that
refreshes an admitted peer.

Declare those five fields and keep additionalProperties false, so typos
and unknown key fields are still refused. osOwner is left out: the
registry never sets it on a key.
#71]

A network recognises its own entry from its configuration, so a stored
self value is redundant. federationRole now only records our decision
about a peer: source, requester or both.
@tdprajwal
tdprajwal force-pushed the feat/71-registry-federation-fields branch from 7f91a05 to 27e97ee Compare October 15, 2026 10:36
federationRole is now an object with optional source and requester
halves instead of a source/requester/both string; both halves present
replaces "both". Each half carries its own schemaPacks (moved from the
top level), descriptorRevision, admittedAt and updatedAt, since the
design doc stores admission independently for each direction.
federationRole drops out of indexFields, as it is no longer a scalar.
tdprajwal added a commit that referenced this pull request Oct 21, 2026
…nAgriNet/network-adapter#74]

setup.py registers one Participant for BECKN_NETWORK_ID, role [consumer,
network, provider], with one keypair the three adapters share. The entry's
osid is the adapters' key id. Takes the Participant schema from helmcharts
#74, so BECKN_NETWORK_ID must be a DNS name; .env.example follows.
@manjudr

manjudr commented Oct 22, 2026

Copy link
Copy Markdown
Member

@tdprajwal - Please simplify the PR description. I can't read it.

@tdprajwal

Copy link
Copy Markdown
Author

I will be raising this partcular #74 PR on a new PR

@tdprajwal tdprajwal closed this Oct 22, 2026
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.

Registry schema changes for federation

2 participants