# Relationship Protocol v1

Identifier: `relationship/v1`. Status: implemented initial native contract; not an external standard.
It specifies meaning and authorized operations, not a database, transport, or model runtime.

## Records

An entity has `id`, `workspace_id`, `collection_id`, `namespace`, `version`, `type`, `kind=entity`,
`revision`, `data`, empty `participants`, optional lifecycle `state`, `archived`, and timestamps.
A relationship has the same envelope and `kind=relationship`, with named `participants` roles.
Each role contains an array. Read references are `{id: UUID}` or `{restricted: true}`. Creation
and role patches accept UUID strings; redacted projections are never replacement input.

IDs are stable. `namespace/version/type` pins a type in an immutable program package. Participants
can be entities or relationships; cycles are allowed but never implicitly traversed. There is no
global identity deduplication or contact merge. Applications may search before creating records.
An entity describing a person/agent is not a credential or authority principal.

## Programs

A package contains `namespace`, positive integer `version`, `types`, and optional `views`.
Each type declares `kind`, `schema`, optional `roles`, `lifecycle`, and `commands`.
Roles declare `min`, `max`, and allowed `namespace/type` strings. A lifecycle declares `initial`
and `states`. Commands declare an `input` schema, optional `guard`, `from`/`to`, `set_data`,
and finite `emit` entries. Views declare `type`, optional `where`, and projected `fields`.
See the bundled examples for exact JSON representations. Unknown package/type/command keywords
are rejected rather than silently introducing behavior.

Schema subset: `type`, `properties`, `required`, `additionalProperties`, `enum`, `items`,
`minLength`, `maxLength`, `minimum`, `maximum`, `minItems`, `maxItems`, and string `format=date-time`.
Objects must declare properties/required and `additionalProperties=false`. Types are object,
array, string, integer, number, boolean, and null. Default maximum string length is 4,000 and array
length is 100. Definition depth is at most 10. This is explicitly not a complete JSON Schema engine.

Rules use exactly one operator per expression map: `path`, `all`, `any`, `not`, `present`,
`eq`, `ne`, `gt`, `gte`, `lt`, `lte`. Paths start with `data`, `input`, or `state` and have at
most 8 components. Depth is at most 10; Boolean operand lists contain at most 30 expressions.
Numeric comparisons fail closed for nonnumeric values. Constants are JSON scalar/array values.

`set_data` maps declared target fields to expressions; the result must still satisfy the schema.
`from`/`to` must name declared states. `emit` is at most 10 `{kind:"work.requested",task:...}`
entries. Each emitted event references its originating record, program version and command. It
is a durable intent, not hosted execution, tool consent, or verified external completion.

## Operations and transports

The shared operation registry is `platform/src/won/api.clj`. It defines required arguments and
input shapes for both transports. REST uses `POST /v1/operations` with
`{operation:"record.get",arguments:{workspace_id:...,record_id:...}}`. Browser callers use their
session, exact Origin and `X-CSRF-Token`. OAuth bearer clients can use the same REST boundary.
MCP uses `/mcp`, `tools/list`, and `tools/call`. Operation dots become underscores in tool names.
All operations have an object response. Unknown operations/arguments are rejected.

Required mutation fields include `idempotency_key`; record changes also require `expected_revision`.
`record.update` accepts `patch:{set:{...},remove:[fieldNames]}` and optional
`roles:{add:{role:[ids]},remove:{role:[ids]}}`. It cannot change lifecycle state.
`command.list` explains requirements using optional supplied input. `command.execute` applies a
named command only if current authority, input, state, guard, and revision all permit it.

Read operations include capabilities, workspaces, collections, program versions, example packages,
record retrieval/search, command discovery, history and change feed. Mutations include workspace
creation/membership, collection creation/access, program drafting/activation, record creation/patch/
archive and command execution. Human membership changes and workspace creation remain console-only.
Authorized agents may draft/activate programs using explicitly delegated scopes.

## Paging, authorization and history

Lists/search accept `limit` (1–100, default 20) and an opaque-to-clients `cursor`. Search filters
include namespace/type/state/query/collection, or an explicit namespace/version/view. Queries
search only authorized record data; they do not search or expand inaccessible participants.
Views preserve record envelopes and authorization but project only selected data fields.
Cursors are bound to workspace and filter/history context. Do not construct or edit cursors.

Change events have stable `event_id`, ordered numeric `id`, actor/client attribution, optional
record/revision, kind, timestamp, and payload. A mutation stores a record snapshot; work requests
store their task intent. Current access and participant redaction apply even to old snapshots.
Feed responses include `next_cursor` when more data may remain, and `checkpoint` to poll for later
changes. Consumers store the checkpoint and deduplicate event IDs. A sparse scan may return no
items with a continuation. History is immutable internally, but its authorized projection may
change with permissions. No event or relationship confers external authority.

Invalid arguments return 400, missing/restricted records 404, insufficient delegated authority 403,
stale revisions/transition conditions/idempotency conflicts 409, and schema failures 422.
MCP tool failures return `isError=true` with the public explanation. Unauthenticated MCP requests
return 401 with protected-resource metadata discovery.
