ak.schema.strand.v1
ak.schema.strand.v1 · file: schemas/strand.schema.json * $ · object
allOf · allOf[0] ·
?allOf · allOf[1] ·
?allOf · allOf[2] ·
?allOf · allOf[3] ·
?allOf · allOf[4] ·
?allOf · allOf[5] ·
?allOf · allOf[6] ·
?id ·
stringPresent on the materialised object. MUST be absent from the create Event payload: zh/models/common-fields.md derives it from the create Event's own event_id (retyped), so a payload-supplied id would be a second, forgeable truth.
pattern:
^ak:strand:[A-Za-z0-9_-]{44}$* schema ·
const "ak.schema.strand.v1"enum:
"ak.schema.strand.v1"* realm_id ·
string · $ref ./common-ids.schema.json#/$defs/realm_idRetyped ak.realm.create Event token. It therefore carries the same fixed current-v1 0x01/SHA-256 content-address identity and is not selected by Realm state.
pattern:
^ak:realm:[A-Za-z0-9_-]{44}$scope_circle_id ·
string · $ref ./common-ids.schema.json#/$defs/circle_idOptional reference to an intra-Realm Circle (see models/circle.md) that defines this Strand's effective scope. When set, ALL Strand tracks (synthesis, discussion, etc.) share that Circle's membership, history visibility, delivery/query/projection boundary, and MLS activation state. When unset, the Strand lives in Realm-default scope. The producer signs the corresponding Event.scope_ref; the receiver verifies that the Circle belongs to this Realm and that the derived scope equals Event.scope_ref before materializing the object's immutable effective_scope. Rebinding scope_circle_id is forbidden by default (failed_precondition reason=scope_rebind_forbidden). For 'wide synthesis + narrow discussion' scenarios use two Strands linked by a confidential_discussion_of Relation (circle.md §7.2).
pattern:
^ak:circle:[A-Za-z0-9_-]{44}$schema_refs · array<string>
Optional authoritative schema set for profile-defined metadata.fields subtrees. Unlike Morph, a plain Strand carries no profile subtree and omits this field entirely; when present it MUST list at least one profile schema id. The container self-schema ak.schema.strand.v1 MUST NOT appear here. Every listed profile schema and its metadata.fields namespace MUST co-occur in both directions: the ref without the subtree and the subtree without the ref are both schema_violation, evaluated on the post-patch object by the registered event-kind payload class and reducer. Current-v1 has no per-Event requirements.schema[] carrier.
items ·
stringpattern:
^ak\.schema\.[a-z0-9_]+(?:\.[a-z0-9_]+)*\.v[0-9]+$agent_participation · object
Optional wrapped five-bit Agent ceiling. When present every bit is explicit and may only tighten the enclosing Circle or Realm ceiling.
* agent · object · $ref ./principal-operations.schema.json#/$defs/participation_bits
* reply_message ·
boolean* reaction_add ·
boolean* reaction_remove ·
boolean* accept_third_party_mention ·
boolean* act_on_behalf ·
booleanmetadata · object · $ref #/$defs/strand_metadata
Extensible user-readable Strand metadata. In MLS / E2EE realms this object is encrypted as encrypted_metadata unless the Realm explicitly opts into plaintext metadata.
title ·
string (arkret-single-line-display-text) · format=arkret-single-line-display-text · $ref string-profiles.schema.json#/$defs/display_text_512NFC multilingual single-line display text; mixed scripts, emoji, and symbols are allowed.
pattern:
^[^\u0000-\u001F\u007F-\u009F\u202A-\u202E\u2066-\u2069\uFEFF]*[^\s\u0000-\u001F\u007F-\u009F\u202A-\u202E\u2066-\u2069\uFEFF][^\u0000-\u001F\u007F-\u009F\u202A-\u202E\u2066-\u2069\uFEFF]*$summary ·
string (arkret-short-text) · format=arkret-short-text · $ref string-profiles.schema.json#/$defs/short_textNFC multilingual short text. LF is allowed; CR, other C0/C1 controls, BOM, and bidi embedding/override controls are rejected.
pattern:
^[^\u0000-\u0009\u000B-\u001F\u007F-\u009F\u202A-\u202E\uFEFF]*$fields · object · $ref #/$defs/metadata_fields
Profile-defined Strand metadata fields. Reducer-owned fields are forbidden here to avoid dual sources of truth.
calendar · object · $ref ./calendar-event.schema.json
Schedule subtree carried at Strand metadata.fields.calendar and activated by ak.schema.calendar_event.v1 in Strand.schema_refs. Both directions of that binding are enforced by strand.schema.json. All intervals are half-open [start, end). Timed events carry RFC 8984 LocalDateTime wall-clock anchors resolved through timezone plus tzdb_version; this schema never carries an absolute instant.
allOf · allOf[0] ·
?allOf · allOf[1] ·
?* start · oneOf[2]
Inclusive lower bound of the event interval, and the recurrence anchor. Narrowed to local_date when all_day is true and to local_date_time when it is false.
oneOf · oneOf[0] ·
string (date) · format=date · $ref ./time.schema.json#/$defs/local_dateProleptic Gregorian calendar date with no time, offset, or zone. Not an absolute instant: it resolves to an instant only through an explicitly carried IANA time zone plus TZDB version. Shape validation by this pattern is supplemented by semantic date validation.
pattern:
^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$oneOf · oneOf[1] ·
string · $ref ./time.schema.json#/$defs/local_date_timeRFC 8984 LocalDateTime narrowed to whole seconds. Wall-clock time with no offset, no Z suffix, no bracketed zone, and no fractional seconds; it resolves to an instant only through an explicitly carried IANA time zone plus TZDB version. Producers MUST NOT spell whole seconds as .0 or .000. Shape validation by this pattern is supplemented by semantic date validation.
pattern:
^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$* end · oneOf[2]
Exclusive upper bound of the event interval, in the same shape as start. MUST be strictly later than start in both branches; a single all-day event spells end as the following date. JSON Schema cannot express the cross-field comparison, so reducers and profiles enforce it with schema_violation.
oneOf · oneOf[0] ·
string (date) · format=date · $ref ./time.schema.json#/$defs/local_dateProleptic Gregorian calendar date with no time, offset, or zone. Not an absolute instant: it resolves to an instant only through an explicitly carried IANA time zone plus TZDB version. Shape validation by this pattern is supplemented by semantic date validation.
pattern:
^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$oneOf · oneOf[1] ·
string · $ref ./time.schema.json#/$defs/local_date_timeRFC 8984 LocalDateTime narrowed to whole seconds. Wall-clock time with no offset, no Z suffix, no bracketed zone, and no fractional seconds; it resolves to an instant only through an explicitly carried IANA time zone plus TZDB version. Producers MUST NOT spell whole seconds as .0 or .000. Shape validation by this pattern is supplemented by semantic date validation.
pattern:
^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$* timezone ·
stringCanonical IANA Zone name in the release named by tzdb_version. Producers MUST resolve Link aliases to their canonical Zone before signing per calendar-timezone-registry.json; this pattern only constrains shape.
pattern:
^[A-Za-z][A-Za-z0-9_+-]*(?:/[A-Za-z0-9_+-]+)*$* tzdb_version ·
stringIANA TZDB release tag (for example 2026a) whose rules resolve timezone and every derived instant. Signed with the schedule so receivers never silently substitute their locally installed release.
pattern:
^[0-9]{4}[a-z]$* all_day ·
booleanTrue selects the date branch of start/end/recurrence.until; false selects the whole-second LocalDateTime branch.
* status ·
string (enum)Whole-event schedule status. Required with no implicit default; distinct from the generic Strand stage and state axes. cancelled rejects new RSVPs and is schedule-relevant for notification.
enum:
"confirmed" "tentative" "cancelled"recurrence ·
$ref #/$defs/recurrence · $ref #/$defs/recurrencelocation · oneOf[2]
oneOf · oneOf[0] ·
$ref #/$defs/calendar_location · $ref #/$defs/calendar_locationoneOf · oneOf[1] · object · $ref ./encrypted-envelope.schema.json
Minimal ciphertext wire. purpose, effective scope and Event kind come from the frozen signed outer Event; mls_group_id is derived from that scope. The canonical pre-encryption header is reconstructed and is not duplicated on wire.
* version ·
const "1.0"enum:
"1.0"* content_type ·
stringpattern:
^[a-z0-9.+-]+/[a-z0-9.+-]+$* encryption_context ·
$ref #/$defs/encryption_context · $ref #/$defs/encryption_context* ciphertext ·
stringpattern:
^[A-Za-z0-9_-]+$call_id ·
stringSoft reference to an Arkret call session. It never widens access: an unresolvable or invisible call_id MUST NOT leak call state.
pattern:
^ak:call:[A-Za-z0-9_-]{44}$attendees · array<$ref #/$defs/attendee>
The single canonical schedule roster. Membership and invite projections are producer inputs only, never a second projection truth source. actor_id uniqueness is enforced by the reducer; at most one organizer is enforced here by maxContains.
items ·
$ref #/$defs/attendee · $ref #/$defs/attendeeencrypted_metadata · object · $ref ./encrypted-envelope.schema.json
Minimal ciphertext wire. purpose, effective scope and Event kind come from the frozen signed outer Event; mls_group_id is derived from that scope. The canonical pre-encryption header is reconstructed and is not duplicated on wire.
* version ·
const "1.0"enum:
"1.0"* content_type ·
stringpattern:
^[a-z0-9.+-]+/[a-z0-9.+-]+$* encryption_context ·
$ref #/$defs/encryption_context · $ref #/$defs/encryption_context* ciphertext ·
stringpattern:
^[A-Za-z0-9_-]+$topic · object · $ref #/$defs/strand_topic
Optional single Topic classification for a stably bound Direct Conversation Chat. Root same-Realm Topic Space(kind=topic) only. Whole set/unset via ak.strand.update requires the exact current digest CAS. Omitted means unclassified; no Board position or canonical Relation mirrors this field.
* space_id ·
stringpattern:
^ak:space:[A-Za-z0-9_-]{44}$* rank ·
stringpattern:
^[A-Za-z0-9]{1,128}$content · object · $ref #/$defs/content_block
Strand description body. This is base Strand content and is independent of the synthesis and discussion tracks.
allOf · allOf[0] ·
?allOf · allOf[1] ·
?allOf · allOf[2] ·
?* kind ·
$ref #/$defs/content_kind · $ref #/$defs/content_kind* body ·
stringformat ·
string (enum)enum:
"plain" "markdown" "prosemirror_json"formatted_body · oneOf[2]
oneOf · oneOf[0] ·
stringoneOf · oneOf[1] ·
objectparts · array<$ref #/$defs/content_block>
items · object · $ref #/$defs/content_block
allOf · allOf[0] ·
?allOf · allOf[1] ·
?allOf · allOf[2] ·
?* kind ·
$ref #/$defs/content_kind · $ref #/$defs/content_kind* body ·
stringformat ·
string (enum)enum:
"plain" "markdown" "prosemirror_json"formatted_body · oneOf[2]
oneOf · oneOf[0] ·
stringoneOf · oneOf[1] ·
objectparts · array<$ref #/$defs/content_block>
items · object · $ref #/$defs/content_block
allOf · allOf[0] ·
?allOf · allOf[1] ·
?allOf · allOf[2] ·
?* kind ·
$ref #/$defs/content_kind · $ref #/$defs/content_kind* body ·
stringformat ·
string (enum)enum:
"plain" "markdown" "prosemirror_json"formatted_body · oneOf[2]
oneOf · oneOf[0] ·
stringoneOf · oneOf[1] ·
objectparts · array<$ref #/$defs/content_block>
items · object · $ref #/$defs/content_block
allOf · allOf[0] ·
?allOf · allOf[1] ·
?allOf · allOf[2] ·
?* kind ·
$ref #/$defs/content_kind · $ref #/$defs/content_kind* body ·
stringformat ·
string (enum)enum:
"plain" "markdown" "prosemirror_json"formatted_body · oneOf[2]
oneOf · oneOf[0] ·
…recursion truncated at depth 8; see source schema for full shape
oneOf · oneOf[1] ·
…recursion truncated at depth 8; see source schema for full shape
parts · array<$ref #/$defs/content_block>
items ·
…recursion truncated at depth 8; see source schema for full shape
encrypted_content · object · $ref ./encrypted-envelope.schema.json
Minimal ciphertext wire. purpose, effective scope and Event kind come from the frozen signed outer Event; mls_group_id is derived from that scope. The canonical pre-encryption header is reconstructed and is not duplicated on wire.
* version ·
const "1.0"enum:
"1.0"* content_type ·
stringpattern:
^[a-z0-9.+-]+/[a-z0-9.+-]+$* encryption_context ·
$ref #/$defs/encryption_context · $ref #/$defs/encryption_context* ciphertext ·
stringpattern:
^[A-Za-z0-9_-]+$* tracks · object
Active Strand collaboration surfaces, keyed by an active name in track-name-registry.json. The synthesis entry may carry its own content / encrypted_content; the discussion entry configures a Message timeline and may not carry either field. Tracks do NOT carry independent membership / permissions / history visibility / E2EE: the entire Strand shares a single effective scope determined by Strand.scope_circle_id (see models/circle.md). At most one track entry may explicitly set is_primary=true; when none does, the reducer derives the primary track by the deterministic rules in models/strand-and-message.md §4.5. The map MUST contain at least one entry; propertyNames fails closed for names outside the registry-backed TrackName enum.
discussion · allOf[2]
allOf · allOf[0] · object · $ref #/$defs/strand_track
Strand track entry. The stable name is the map key in Strand.tracks. The synthesis entry may carry its own narrative content / encrypted_content in addition to config; the discussion entry is config-only because its authored content is carried by Message objects. Tracks do NOT carry independent membership / permissions / history visibility / E2EE. The whole Strand has a single effective scope determined by Strand.scope_circle_id (see models/circle.md); there is no per-track security boundary. To represent 'wide synthesis + narrow discussion', create two Strands linked by a confidential_discussion_of Relation per models/circle.md §7.2.
enabled ·
booleanWhether this track currently accepts new writes (synthesis edits / discussion messages). Default true when omitted. Setting enabled=false MUST cause the reducer to reject subsequent writes to this track with 'track_disabled' until enabled is set back to true. Disabling a track does NOT delete prior history; it freezes new writes only. UI MAY hide a disabled track or render it read-only.
example:
trueis_primary ·
booleanprofile ·
stringpattern:
^[a-z][a-z0-9_.-]{0,127}$template ·
stringmetadata ·
objectTrack-local UI metadata only. The reducer NEVER interprets its keys; it does not create membership, history visibility, access, or E2EE scope. Access-like keys are rejected at the wire layer to prevent a pseudo-access surface (tracks have no independent access — see models/strand-and-message.md §4.4).
content · object · $ref #/$defs/content_block
Track-owned ContentBlock. In the active v1 registry only tracks.synthesis may carry this field; tracks.discussion content is carried by Message objects.
allOf · allOf[0] ·
?allOf · allOf[1] ·
?allOf · allOf[2] ·
?* kind ·
$ref #/$defs/content_kind · $ref #/$defs/content_kind* body ·
stringformat ·
string (enum)enum:
"plain" "markdown" "prosemirror_json"formatted_body · oneOf[2]
oneOf · oneOf[0] ·
stringoneOf · oneOf[1] ·
objectparts · array<$ref #/$defs/content_block>
items · object · $ref #/$defs/content_block
allOf · allOf[0] ·
?allOf · allOf[1] ·
?allOf · allOf[2] ·
?* kind ·
$ref #/$defs/content_kind · $ref #/$defs/content_kind* body ·
stringformat ·
string (enum)enum:
"plain" "markdown" "prosemirror_json"formatted_body · oneOf[2]
oneOf · oneOf[0] ·
stringoneOf · oneOf[1] ·
objectparts · array<$ref #/$defs/content_block>
items · object · $ref #/$defs/content_block
allOf · allOf[0] ·
…recursion truncated at depth 8; see source schema for full shape
allOf · allOf[1] ·
…recursion truncated at depth 8; see source schema for full shape
allOf · allOf[2] ·
…recursion truncated at depth 8; see source schema for full shape
* kind ·
…recursion truncated at depth 8; see source schema for full shape
* body ·
…recursion truncated at depth 8; see source schema for full shape
format ·
…recursion truncated at depth 8; see source schema for full shape
formatted_body ·
…recursion truncated at depth 8; see source schema for full shape
parts ·
…recursion truncated at depth 8; see source schema for full shape
encrypted_content · object · $ref ./encrypted-envelope.schema.json
Minimal ciphertext wire. purpose, effective scope and Event kind come from the frozen signed outer Event; mls_group_id is derived from that scope. The canonical pre-encryption header is reconstructed and is not duplicated on wire.
* version ·
const "1.0"enum:
"1.0"* content_type ·
stringpattern:
^[a-z0-9.+-]+/[a-z0-9.+-]+$* encryption_context ·
$ref #/$defs/encryption_context · $ref #/$defs/encryption_context* ciphertext ·
stringpattern:
^[A-Za-z0-9_-]+$allOf · allOf[1] ·
?synthesis · object · $ref #/$defs/strand_track
Strand track entry. The stable name is the map key in Strand.tracks. The synthesis entry may carry its own narrative content / encrypted_content in addition to config; the discussion entry is config-only because its authored content is carried by Message objects. Tracks do NOT carry independent membership / permissions / history visibility / E2EE. The whole Strand has a single effective scope determined by Strand.scope_circle_id (see models/circle.md); there is no per-track security boundary. To represent 'wide synthesis + narrow discussion', create two Strands linked by a confidential_discussion_of Relation per models/circle.md §7.2.
enabled ·
booleanWhether this track currently accepts new writes (synthesis edits / discussion messages). Default true when omitted. Setting enabled=false MUST cause the reducer to reject subsequent writes to this track with 'track_disabled' until enabled is set back to true. Disabling a track does NOT delete prior history; it freezes new writes only. UI MAY hide a disabled track or render it read-only.
example:
trueis_primary ·
booleanprofile ·
stringpattern:
^[a-z][a-z0-9_.-]{0,127}$template ·
stringmetadata ·
objectTrack-local UI metadata only. The reducer NEVER interprets its keys; it does not create membership, history visibility, access, or E2EE scope. Access-like keys are rejected at the wire layer to prevent a pseudo-access surface (tracks have no independent access — see models/strand-and-message.md §4.4).
content · object · $ref #/$defs/content_block
Track-owned ContentBlock. In the active v1 registry only tracks.synthesis may carry this field; tracks.discussion content is carried by Message objects.
allOf · allOf[0] ·
?allOf · allOf[1] ·
?allOf · allOf[2] ·
?* kind ·
$ref #/$defs/content_kind · $ref #/$defs/content_kind* body ·
stringformat ·
string (enum)enum:
"plain" "markdown" "prosemirror_json"formatted_body · oneOf[2]
oneOf · oneOf[0] ·
stringoneOf · oneOf[1] ·
objectparts · array<$ref #/$defs/content_block>
items · object · $ref #/$defs/content_block
allOf · allOf[0] ·
?allOf · allOf[1] ·
?allOf · allOf[2] ·
?* kind ·
$ref #/$defs/content_kind · $ref #/$defs/content_kind* body ·
stringformat ·
string (enum)enum:
"plain" "markdown" "prosemirror_json"formatted_body · oneOf[2]
oneOf · oneOf[0] ·
stringoneOf · oneOf[1] ·
objectparts · array<$ref #/$defs/content_block>
items · object · $ref #/$defs/content_block
allOf · allOf[0] ·
?allOf · allOf[1] ·
?allOf · allOf[2] ·
?* kind ·
$ref #/$defs/content_kind · $ref #/$defs/content_kind* body ·
stringformat ·
string (enum)enum:
"plain" "markdown" "prosemirror_json"formatted_body · oneOf[2]
oneOf · oneOf[0] ·
…recursion truncated at depth 8; see source schema for full shape
oneOf · oneOf[1] ·
…recursion truncated at depth 8; see source schema for full shape
parts · array<$ref #/$defs/content_block>
items ·
…recursion truncated at depth 8; see source schema for full shape
encrypted_content · object · $ref ./encrypted-envelope.schema.json
Minimal ciphertext wire. purpose, effective scope and Event kind come from the frozen signed outer Event; mls_group_id is derived from that scope. The canonical pre-encryption header is reconstructed and is not duplicated on wire.
* version ·
const "1.0"enum:
"1.0"* content_type ·
stringpattern:
^[a-z0-9.+-]+/[a-z0-9.+-]+$* encryption_context ·
$ref #/$defs/encryption_context · $ref #/$defs/encryption_context* ciphertext ·
stringpattern:
^[A-Za-z0-9_-]+$state ·
string (enum)Strand lifecycle state. 'active' is the default. Reducer enforces transitions per common-fields.md §5.1: ak.strand.archive MUST come from 'active' (else failed_precondition reason=strand_not_active); ak.strand.restore MUST come from 'archived' (else strand_not_archived); terminal state is reached only via a ak.redaction event targeting the strand (MUST come from {'active','archived'} else strand_already_terminal). Same-state self-transitions MUST fail. 'redacted' is the irreversible terminal.
enum:
"active" "archived" "redacted"state_changed_at ·
string (date-time) · format=date-time · $ref #/$defs/timestampCanonical Arkret-owned absolute instant. UTC Z form with exactly three millisecond digits. Whole seconds MUST use .000Z; offsets, missing/finer fractions, lowercase separators, leap seconds, and invalid Gregorian calendar dates are forbidden. Shape validation by this pattern is supplemented by semantic date validation.
pattern:
^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\.[0-9]{3}Z$stage ·
string (enum)Optional Strand business-progression stage. Orthogonal to top-level 'state' (physical lifecycle): archive does NOT change stage; stage=done does NOT auto-archive. ak.strand.create MAY omit stage; when present it must be one of the protocol-level enum values and has no protocol-level default. The only wire path that mutates stage after creation is the dedicated ak.strand.stage.set event; ak.strand.update patches on stage / stage_changed_at MUST be rejected (schema_violation, single-source). Stage transitions intentionally do NOT carry a reason / note field — the ak.strand.stage.set event log is the audit source, and human-readable explanations belong in a Message on the discussion track that references the event. v1 has no per-Realm workflow profile carrier: the core reducer hard invariants (terminal state freeze, non-active reject) defined in models/common-fields.md §5.3.3 are the complete transition rule, and receivers MUST NOT narrow admission from Realm-private configuration (§5.3.4). See models/strand-and-message.md §3.2 for full semantics and models/common-fields.md §5.3 for the protocol-level enum / bucket mapping.
enum:
"draft" "proposed" "planned" "in_progress" "blocked" "done" "cancelled" "superseded"stage_changed_at ·
string (date-time) · format=date-time · $ref #/$defs/timestampCanonical Arkret-owned absolute instant. UTC Z form with exactly three millisecond digits. Whole seconds MUST use .000Z; offsets, missing/finer fractions, lowercase separators, leap seconds, and invalid Gregorian calendar dates are forbidden. Shape validation by this pattern is supplemented by semantic date validation.
pattern:
^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\.[0-9]{3}Z$* created_by · oneOf[2] · $ref ./common-ids.schema.json#/$defs/actor_id
Complete protocol identity for an Event author or Realm member: account carries the exact AccountId for every Station-hosted principal; service identifies a service acting as itself. The discriminator is validated against accepted registration and admission evidence; it never authorizes itself. Account and service are distinct, and no comparison may fall back to a bare principal_id. Agent and integration classification, provisioning, controller binding and credential authorization are independently verified facts, not identity variants. Account actors at different Stations MUST NOT share or inherit authority merely because their principal_id, DID controller or signing key matches, including membership, capability, RealmCommit-signing and recovery authority.
oneOf · oneOf[0] · object
* kind ·
const "account"enum:
"account"* account_id ·
$ref #/$defs/account_id · $ref #/$defs/account_idoneOf · oneOf[1] · object
* kind ·
const "service"enum:
"service"* service_id ·
$ref #/$defs/did_core_id · $ref #/$defs/did_core_id* created_at ·
string (date-time) · format=date-time · $ref #/$defs/timestampCanonical Arkret-owned absolute instant. UTC Z form with exactly three millisecond digits. Whole seconds MUST use .000Z; offsets, missing/finer fractions, lowercase separators, leap seconds, and invalid Gregorian calendar dates are forbidden. Shape validation by this pattern is supplemented by semantic date validation.
pattern:
^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\.[0-9]{3}Z$updated_by · oneOf[2] · $ref ./common-ids.schema.json#/$defs/actor_id
Complete protocol identity for an Event author or Realm member: account carries the exact AccountId for every Station-hosted principal; service identifies a service acting as itself. The discriminator is validated against accepted registration and admission evidence; it never authorizes itself. Account and service are distinct, and no comparison may fall back to a bare principal_id. Agent and integration classification, provisioning, controller binding and credential authorization are independently verified facts, not identity variants. Account actors at different Stations MUST NOT share or inherit authority merely because their principal_id, DID controller or signing key matches, including membership, capability, RealmCommit-signing and recovery authority.
oneOf · oneOf[0] · object
* kind ·
const "account"enum:
"account"* account_id ·
$ref #/$defs/account_id · $ref #/$defs/account_idoneOf · oneOf[1] · object
* kind ·
const "service"enum:
"service"* service_id ·
$ref #/$defs/did_core_id · $ref #/$defs/did_core_idupdated_at ·
string (date-time) · format=date-time · $ref #/$defs/timestampCanonical Arkret-owned absolute instant. UTC Z form with exactly three millisecond digits. Whole seconds MUST use .000Z; offsets, missing/finer fractions, lowercase separators, leap seconds, and invalid Gregorian calendar dates are forbidden. Shape validation by this pattern is supplemented by semantic date validation.
pattern:
^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\.[0-9]{3}Z$Source
- registry row:
spec/v1/artifacts/registry/schema-registry.json - schema document:
spec/v1/artifacts/schemas/strand.schema.json