Governed Crossing

Governed Crossing — Recording Access Changes: Draft for Feedback

Draft for feedback. This is not the specification. The live lexicons (crossingRecord.json, commitment.json, defs.json) and CONFORMANCE.md are the only normative text. Nothing in this file is part of them. This draft may change, or be withdrawn, as a result of feedback.

Design status

Open questions

Respond in the repository’s Discussions, in the thread “Feedback: recording access changes (draft)”. Refer to questions by number. Each is set out in full in §4.

  1. Q1. Is the emitter’s own agent grant, taken alone, the right test of who counts as its agent?
  2. Q2. Should one value, access-change-observed, carry two meanings, told apart by actorRelation, or should a gated change that diverged get its own value?
  3. Q3. Should a gated change that produced something other than what the gate authorized be allowed a public commitment?
  4. Q4. Is the last sentence of live CONFORMANCE.md line 66 accurate and clear?
  5. Q5 (optional). Does a grantee giving a whole group access to a document read as an access change, and to what?

1. What this is

A governed crossing record is evidence that data crossed from a person’s own system into shared infrastructure. The live conformance rules (CONFORMANCE.md) say, in §8, that a change in who can see data, where the data itself does not move, is not yet covered, and that a design for recording it is in progress.

This file is that design, in draft:

At the lexicon level the change is additive: no existing field or value changes. At the conformance level one existing rule changes: gateCheck becomes required on the existing gate-check record type. The new values (access-change as a recordType; access-change and access-change-observed as a governanceEvent) validate today, because the value lists are open, but they will not conform until they are registered in the vocabulary.

2. How to respond

What happens to responses. Every response is logged with its date, channel, your public handle, and the response verbatim or linked. Nothing is decided during the window, and questions are not settled one by one in the thread. After the window closes, the responses are weighed before any of this text is adopted. Responses may be quoted with attribution to your public handle. If you would rather not be quoted, say so in your response.

3. The model in brief

4. Questions

Q1 · Who counts as the emitter’s agent

An emitter must record a change made around its own gate by itself or by its agent. A change made by anyone else may be recorded, but need not be. The draft wording:

In the draft, the emitter’s own grant is the only test. An actor is the emitter’s agent only if the emitter itself issued it a grant to act as its agent. It follows that:

The draft does not follow responsibility through a representative to the systems the representative appoints. It also discloses that none of this can be checked from outside:

Asked: Is the emitter’s own agent grant the right test, taken alone? In systems you know, do automated actors act for a party without a grant from that party, for example under a grant from an administrator or a service, and should the duty to record reach them?

Q2 · One value, access-change-observed, with two meanings

The draft rule:

So access-change-observed covers two different situations:

  1. a change the emitter’s own gate did not pass, made around the gate by the emitter or its agent (emitter-ungated) or by someone else (other-party);
  2. a change that did go through the emitter’s gate, where the regimes observed differ from those the gate authorized. Here the record stays emitter-gated: the act was gated, but the difference was not.

A reader tells them apart by actorRelation. The draft discloses what the second case does not show:

Asked: Is one value with two meanings workable for people and services reading these records, with actorRelation doing the disambiguation? Or should the second case have its own value?

Q3 · Gated changes that diverge, and the public record

For a private access change, a commitment may be made only to an emitter-gated record whose governanceEvent is access-change. The ground is that a commitment attests the emitter’s record of its own governed act. A gated change whose observed regimes differ from those authorized is therefore never committed, since the difference was not governed. The draft adds this to the last Known limit:

Asked: Is it right that a gated change which produced something other than what the gate authorized can never be committed? Would you rather an emitter be able to commit to it, so that divergence can appear in the public record? Is the Known limit clear about the consequence?

Q4 · Live text: CONFORMANCE.md line 66

This wording is already published in the Known limits (line 66 at b5723e3). It has not been reviewed:

An act can succeed without the emitter observing it, for example a write that landed after a timeout. A missing completion is not evidence that nothing crossed. Third parties cannot tell from an emitter’s public records whether it failed to emit a completion it owed.

Asked: Is the last sentence accurate and clear as you read it? It is meant to say that nothing in an emitter’s public records lets anyone else detect a completion record that the emitter should have emitted and did not. If it reads otherwise to you, or you can suggest better wording, please say so.

Q5 · Optional: a grantee giving a whole group access to a document

This concerns Keyhive core at revision 9a8c1d56 (inkandswitch/keyhive, keyhive_core 0.6.0, 2026-09-26). On our reading of the source at that revision, not yet tested at runtime:

Under the draft, the first is an access change on the document (granularity: document). The set of readers widens but stays bounded, so the regimes do not differ, as when a member is added to a group. The change is still recorded. If it was done by a person holding a grant from the document’s owner, the owner’s record would be other-party, and recording it is optional.

Asked: Does a grantee pushing a document into a group read to you as an access change? If so, is it the document’s access that changed, the group’s, or both? And is optional recording by the document’s owner the right place to leave it?


Appendix A · Draft lexicon additions

These additions sit on top of the live defs.json and crossingRecord.json at b5723e3. No existing field or value changes.

A.1 org.governedcrossing.temp.defs: five new definitions

Entries to add under defs:

"gateCheck": {
    "type": "object",
    "description": "Instance extension for recordType gate-check (governanceEvent gate-check). One record per gate invocation outside a substrate crossing, pass or block; a substrate crossing's gate is recorded in its crossing-intent. Records what the gate checked; says nothing about whether any act that followed took effect. A gate-check record is not published or committed.",
    "required": [
      "actorDID",
      "grantReference",
      "capability",
      "gateCheckedAt",
      "gateResult"
    ],
    "properties": {
      "actorDID": {
        "type": "string",
        "format": "did",
        "description": "Party whose invocation was checked. May differ from emittedBy (for example an agent acting under a grant)."
      },
      "grantReference": {
        "type": "string",
        "maxLength": 1024,
        "description": "Grant the invocation was checked against. Required so the responsible grantor is resolvable from the record."
      },
      "capability": {
        "type": "string",
        "maxLength": 256,
        "description": "Capability checked."
      },
      "gateCheckedAt": {
        "type": "string",
        "format": "datetime"
      },
      "gateResult": {
        "type": "string",
        "knownValues": [
          "pass",
          "blocked"
        ],
        "maxLength": 32,
        "description": "A record of a blocked gate check is never published in any form."
      },
      "blockBasis": {
        "type": "string",
        "maxLength": 1000,
        "maxGraphemes": 300,
        "description": "Required when gateResult is blocked: the condition that caused the block (for example a revocation state reference)."
      },
      "subject": {
        "type": "ref",
        "ref": "#subjectRef",
        "description": "What the invocation applies to. Required when authorizedChange is present."
      },
      "authorizedChange": {
        "type": "ref",
        "ref": "#regimeChange",
        "description": "Present when the invocation is an access change: the change the gate authorized."
      },
      "actTimeoutHorizon": {
        "type": "string",
        "format": "datetime",
        "description": "Required when authorizedChange is present: the time by which the emitter expects to observe the change in effect."
      }
    }
  },
  "accessChange": {
    "type": "object",
    "description": "Instance extension for recordType access-change. Records an observed change in who can read a subject, or who holds a capability over it, where the subject stays at the same address. Nothing is written to a new location. governanceEvent is access-change when actorRelation is emitter-gated and the regimes observed are those the gate authorized, and access-change-observed otherwise.",
    "required": [
      "subject",
      "regimeBefore",
      "regimeAfter",
      "observedAt",
      "actorRelation"
    ],
    "properties": {
      "subject": {
        "type": "ref",
        "ref": "#subjectRef"
      },
      "regimeBefore": {
        "type": "ref",
        "ref": "#regime"
      },
      "regimeAfter": {
        "type": "ref",
        "ref": "#regime",
        "description": "The regime observed in effect, which may differ from the one authorized."
      },
      "observedAt": {
        "type": "string",
        "format": "datetime",
        "description": "When the emitter observed the change in effect."
      },
      "actorRelation": {
        "type": "string",
        "knownValues": [
          "emitter-gated",
          "emitter-ungated",
          "other-party"
        ],
        "maxLength": 32,
        "description": "emitter-gated: the act went through the emitter's own gate, and chainReference names that gate-check record, including when the regimes observed differ from those authorized. emitter-ungated: the emitter, or its agent, made the change without going through the emitter's gate. An agent is a non-human actor (for example an AI model or automated system) to which the emitter issued a grant to act as its agent. It stays the emitter's agent while it holds any grant from the emitter, counting a revoked grant until the revocation takes effect on the path the actor used. A change counts if it was made while the actor was the emitter's agent; the time the emitter observes the change is used only where the time of the change is unknown. The emitter answers for its agent's changes whether or not a grant permitted them. A person or organization holding a grant from the emitter, including one who represents it, is not its agent. other-party: someone other than the emitter and its agents made the change, and the emitter observed it."
      },
      "actorDID": {
        "type": "string",
        "format": "did",
        "description": "Who made the change, when known. Absent when either regime has readers unbounded."
      },
      "capability": {
        "type": "string",
        "maxLength": 256,
        "description": "The capability whose holders changed, when the change is to a capability other than reading (for example writing)."
      },
      "grantAction": {
        "type": "string",
        "knownValues": [
          "issued",
          "amended",
          "revoked"
        ],
        "maxLength": 32,
        "description": "When the change is a grant change. Absent when either regime has readers unbounded."
      },
      "affectedGrant": {
        "type": "string",
        "maxLength": 1024,
        "description": "Absent when either regime has readers unbounded."
      },
      "affectedPartyDID": {
        "type": "string",
        "format": "did",
        "description": "Absent when either regime has readers unbounded."
      }
    }
  },
  "subjectRef": {
    "type": "object",
    "description": "What an access change or gate check applies to. The subject stays where it is; only who can see or act on it changes.",
    "required": [
      "uri",
      "granularity"
    ],
    "properties": {
      "uri": {
        "type": "string",
        "format": "uri",
        "maxLength": 2048,
        "description": "The subject's address, the same under both regimes (an at:// URI, a space URI, or a local document or group URI). A change that gives the subject a new address is not an access change."
      },
      "cid": {
        "type": "string",
        "maxLength": 4096,
        "description": "Content address or heads of the subject when this record is emitted (at the gate check, or at observation). Required when granularity is record or document."
      },
      "granularity": {
        "type": "string",
        "knownValues": [
          "record",
          "collection",
          "space",
          "document",
          "group"
        ],
        "maxLength": 32
      }
    }
  },
  "regime": {
    "type": "object",
    "description": "An access regime, described by four properties. Two regimes differ when any of the four differs. The service that enforces the regime is not one of the four: moving to another host under the same properties is not a regime change.",
    "required": [
      "readers",
      "indexing",
      "revocation",
      "integrity"
    ],
    "properties": {
      "readers": {
        "type": "string",
        "knownValues": [
          "bounded",
          "unbounded"
        ],
        "maxLength": 32,
        "description": "bounded: an enumerable set of parties can read. unbounded: anyone can read."
      },
      "indexing": {
        "type": "string",
        "knownValues": [
          "none",
          "global"
        ],
        "maxLength": 32,
        "description": "none: not pushed without a request to relays, indexers or other services that serve anyone. A regime that delivers only to its own bounded set of readers, requested or not, is none. global: pushed without a request to relays, indexers or other services that serve anyone (for example through a firehose); allowed only with readers unbounded."
      },
      "revocation": {
        "type": "string",
        "knownValues": [
          "enforced-stream-close",
          "propagated-request",
          "none"
        ],
        "maxLength": 64,
        "description": "What removing access can do under this regime, including to copies made while it applied. The values share their names with recallSemantics on a crossing intent; their meaning for this field is its own and is registered separately."
      },
      "integrity": {
        "type": "string",
        "knownValues": [
          "signed",
          "deniable",
          "local"
        ],
        "maxLength": 32,
        "description": "Who can verify the subject's authorship. signed: anyone holding a copy. deniable: authorized readers, but not other holders of a copy. local: no reader; only the substrate holding it."
      },
      "service": {
        "type": "string",
        "format": "uri",
        "maxLength": 2048,
        "description": "Service that enforces the regime, if any. Informational."
      }
    }
  },
  "regimeChange": {
    "type": "object",
    "description": "The access change a gate check authorizes, bound at the gate.",
    "required": [
      "regimeBefore",
      "regimeAfter"
    ],
    "properties": {
      "regimeBefore": {
        "type": "ref",
        "ref": "#regime"
      },
      "regimeAfter": {
        "type": "ref",
        "ref": "#regime"
      },
      "regimeAcknowledgment": {
        "type": "string",
        "maxLength": 3000,
        "maxGraphemes": 1000,
        "description": "Declared acknowledgment of the regime change. Required when the two regimes differ; the gate checks presence, not authorship."
      }
    }
  }

A.2 org.governedcrossing.temp.crossingRecord: two new properties (after crossingCompletion)

"gateCheck": {
  "type": "ref",
  "ref": "org.governedcrossing.temp.defs#gateCheck",
  "description": "Required when recordType is gate-check."
},
"accessChange": {
  "type": "ref",
  "ref": "org.governedcrossing.temp.defs#accessChange",
  "description": "Required when recordType is access-change."
}

A.3 Description edits (applied only when the text is adopted)

A.4 Values held until registration

Appendix B · Draft conformance wording

Drafts against the live CONFORMANCE.md. Section numbers refer to that file.

§1 Values: add at the end.

Fields inside gateCheck and accessChange that list values (granularity, readers, indexing, revocation, integrity, actorRelation, grantAction, gateResult) are drafts. They may change before the lexicons leave temp.

§2 Required-when rules: add.

§5 Completion, and what is never published: add after the substrate-crossing bullets.

§6 Commitments: replace the first bullet’s last sentence.

No commitment is made to a crossing-intent or to a gate-check record. Commitments to access-change records follow §8: only to emitter-gated records whose governanceEvent is access-change. None are made yet for other record types.

§8 Access changes: replace the section.

8. Gate checks and access changes

Some changes alter who can see or act on data without moving it: a permissioned space made public, a public space made private again, a member added to a group, an access level raised or lowered, a grant issued or revoked. A change that goes through the emitter’s own gate, with the regimes it authorized, is recorded as a governed event (access-change). A change the emitter observes but its own gate did not pass is recorded as an observation (access-change-observed), whether or not another party’s gate governed it. So is a change that went through the emitter’s gate but produced regimes other than those the gate authorized: the act was gated, the difference was not. Both are recorded with gate-check and access-change records, never with crossing-intent and crossing-completion records, which describe data that moves.

Regimes. A regime is described by four properties:

Two regimes differ when any of the four differs. The service enforcing a regime is not one of the four: moving to another host with the same properties is not a change of regime. Every access change is recorded, whether or not the regime changes. readers decides the exposure claim and what may be published. A difference in any property requires regimeAcknowledgment at the gate.

Change Regimes differ?
Permissioned space made public Yes: readers, indexing, integrity
Space readPolicy changed from public to member-list Yes: readers
Member added to, or removed from, a permissioned space or private group No
Access level raised or lowered inside a private group No
Space moved to a new host under the same properties No
A public record’s “visibility” flag changed while it stays readable and indexed in a public repository No: a flag is not a regime

Exposure claims. A gate-check record carries exposure-upper-bound. An access-change record carries exposure-unbounded when either regime has readers: unbounded, and exposure-upper-bound otherwise. What removing access can do about copies made under the earlier regime is stated by regimeBefore.revocation. The record claims nothing about those copies.

Publication. Publication and the exposure claim follow the observed regimes (accessChange.regimeBefore and regimeAfter). A gated change is observed in effect when the emitter observes that the act its gate passed was applied at the subject, whatever regimes it then observes. That observation is the emitter’s own. The record stays emitter-gated, and the regimes observed, not those authorized, decide the exposure claim and publication. Where they differ from those authorized, its governanceEvent is access-change-observed: the difference was not governed at the gate. Beyond the subject and the two regimes, a record shows what was observed only through capability, grantAction, affectedGrant and affectedPartyDID, when present. Where the two regimes are the same and those fields are absent, the record does not show which change was observed. The observed regimes may differ from the authorized ones. An access change is public when either regime has readers: unbounded, and private otherwise.

Known limits: replace “Private governed crossings of record types other than substrate crossings have no public commitment yet.” with “Private governed crossings of record types other than substrate crossings and access changes have no public commitment yet.” Replace “Access changes are not yet recordable (§8).” with:


Draft for feedback · prepared 2026-09-27 · not the published specification · Built with AI-collaborative methods · Intellectual direction and authorial responsibility: Jedi Wright · UX Minds, LLC