From 7fed3e4478ad28151fab288d569a1268c1f3e4cf Mon Sep 17 00:00:00 2001 From: tv Date: Sat, 7 Nov 2026 22:35:21 +0100 Subject: api: signed values at the store PUT with application/continuum-signed writes an envelope where values are signed (403/409 saying why); reads open envelopes and name the signer (x-continuum-signed-by), ?raw=true shows them. Under strict, unsigned writes reaching a signed value and the users API are 409. Auth plan M7. --- docs/openapi.yaml | 45 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) (limited to 'docs/openapi.yaml') diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 1954dc0..6abfc6d 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -98,11 +98,22 @@ paths: timestamps as `rfc3339` (the default), `unix`, `unix-ms` or `http-date`. Concurrent writes resolve as follows: concurrent maps merge, anything else keeps the newest write. + + A value its user signed (`api.md`, Signed values) shows as the value it holds + (a signed `null` as nothing), and a read of one names its signer in + `x-continuum-signed-by`; as typed JSON, natively and with `raw`, as its + envelope. parameters: - $ref: '#/components/parameters/conflicts' - $ref: '#/components/parameters/heads' - $ref: '#/components/parameters/shallow' - $ref: '#/components/parameters/at' + - name: raw + in: query + description: Values their users signed as stored, as envelopes (`cv1..`), to check them. + schema: + type: boolean + default: false responses: '200': $ref: '#/components/responses/Value' @@ -146,6 +157,11 @@ paths: `.float`, `.boolean`, `.counter` (decimal or `true`/`false`), or `application/vnd.continuum.timestamp; format=rfc3339|unix|unix-ms|http-date`; a body that isn't one is `400`, saying why. + + A value its user signed (`application/continuum-signed`: the payload's line of + JSON, then its armored SSH signature; `api.md`, Signed values) is written as its + envelope where values are signed: by the authenticated user, for this path, + with one of its keys, replacing what is there. A signed `null` deletes (`204`). parameters: - name: retype in: query @@ -172,17 +188,27 @@ paths: schema: type: string example: '2026-11-01T21:50:20.345Z' + application/continuum-signed: + schema: + type: string + example: | + {"path":["config"],"value":{"mode":"fast"},"user":"alice","at":1762400000000,"prev":""} + -----BEGIN SSH SIGNATURE----- + U1NIU0lH… + -----END SSH SIGNATURE----- responses: '200': $ref: '#/components/responses/Value' + '204': + description: A signed `null` written; nothing there now. '400': description: Invalid path, malformed JSON or invalid UTF-8. '401': $ref: '#/components/responses/Unauthorized' '403': - description: The user may not write here (its capabilities, docs/access.md; in a swarm without a user registry, protocol data `_continuum` and the user isn't in `protocol-writers`), or epoch metadata or the swarm ID (`_continuum/epochs`, `_continuum/swarm`, never writable). + description: The user may not write here (its capabilities, docs/access.md; in a swarm without a user registry, protocol data `_continuum` and the user isn't in `protocol-writers`), or epoch metadata or the swarm ID (`_continuum/epochs`, `_continuum/swarm`, never writable); a signed value not by this user, or whose signature doesn't check (the message says why). '409': - description: The path crosses an existing non-map value, or with `retype=false` the write would change a type (the message says where). + description: The path crosses an existing non-map value, or with `retype=false` the write would change a type (the message says where); a value not signed reaching one its user signs under a strict policy; a signed value where values aren't signed, or replacing another than the one there (read again). '415': description: Content-Type is none the API takes (`application/json`, the default; `text/plain`; a type's own, `api.md`, Types). '500': @@ -274,7 +300,7 @@ paths: '307': description: A negative index, made positive (`Location`). '409': - description: Not a list there, or the index is past its end (the message says which, and the length); with `increment`, not a counter there; with `cast`, no cast possible (the message says why). + description: Not a list there, or the index is past its end (the message says which, and the length); with `increment`, not a counter there; with `cast`, no cast possible (the message says why); under a strict policy, reaching a value its user signs. '415': description: Content-Type is none the API takes (`application/json`, the default; `text/plain`; a type's own, `api.md`, Types). '503': @@ -307,6 +333,8 @@ paths: description: The user may not write here (its capabilities, docs/access.md; in a swarm without a user registry, protocol data `_continuum` and the user isn't in `protocol-writers`), or epoch metadata or the swarm ID (`_continuum/epochs`, `_continuum/swarm`, never writable). '404': description: No value at this path. + '409': + description: Under a strict policy, reaching a value its user signs (`PUT` a signed `null` instead). '500': description: Deleting failed. '503': @@ -914,7 +942,7 @@ paths: '401': $ref: '#/components/responses/Unauthorized' '403': { description: May not manage users. } - '409': { description: A user exists, or the caller wouldn't manage users in a new registry. } + '409': { description: "A user exists, the caller wouldn't manage users in a new registry, or the policy has users sign their entries (strict, docs/access.md)." } /admin/users/{name}: parameters: @@ -947,7 +975,7 @@ paths: '201': { description: Added. } '400': { description: Not a user name, or a capability that isn't one. } '403': { description: May not manage users. } - '409': { description: The user exists. } + '409': { description: 'The user exists, or the policy has users sign their entries (strict, docs/access.md).' } delete: tags: [Users] summary: Remove a user @@ -956,7 +984,7 @@ paths: '204': { description: Removed. } '403': { description: May not manage users. } '404': { description: No such user. } - '409': { description: It would keep the caller from managing users. } + '409': { description: It would keep the caller from managing users, or the policy has users sign their entries (strict, docs/access.md). } /admin/users/{name}/disabled: parameters: @@ -973,7 +1001,7 @@ paths: '204': { description: Done. } '403': { description: May not manage users. } '404': { description: No such user. } - '409': { description: It would keep the caller from managing users. } + '409': { description: It would keep the caller from managing users, or the policy has users sign their entries (strict, docs/access.md). } /admin/users/{name}/caps: parameters: @@ -998,6 +1026,7 @@ paths: '400': { description: A capability that isn't one. } '403': { description: May not manage users. } '404': { description: No such user. } + '409': { description: The policy has users sign their entries (strict, docs/access.md). } /admin/users/{name}/caps/{id}: parameters: @@ -1010,7 +1039,7 @@ paths: '204': { description: Revoked. } '403': { description: May not manage users. } '404': { description: No such capability. } - '409': { description: It would keep the caller from managing users. } + '409': { description: It would keep the caller from managing users, or the policy has users sign their entries (strict, docs/access.md). } /admin/dashboard: get: -- cgit v1.3.1