Files
kane-diagnostics/docs/orchestrator/receiver-spec.md
2026-06-16 11:01:43 -04:00

127 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Receiver Spec
**Endpoint:** `POST /receive`
**Contract version:** 1.0
**Status:** design complete, not yet implemented
This is the single endpoint every Hubzilla addon on this node POSTs to. There is no per-addon route. The `addon` field in the envelope body determines how the orchestrator validates and stores the request.
---
## Authentication
Every request must include:
```
X-Node-Token: {shared secret from /etc/civic-orchestrator/env_file}
```
A request with a missing or incorrect token is rejected with `401` before the body is parsed. See `README.md` for the trust model this implies — the token authenticates the node, not the specific addon.
---
## Request Envelope — Common Fields
Every envelope, regardless of `addon`, must include:
| Field | Type | Description |
|---|---|---|
| `addon` | string | One of `vs01`, `dsc01`, `scn01`. Identifies the record type and storage rule. |
| `contract_version` | string | Must be `"1.0"` for this spec version. |
| `association_slug` | string | URL slug of the association. Matches a key in that addon's `config.json` associations object. |
| `association_channel_id` | string | Hubzilla channel ID of the association, stored as string. |
| `participant_id` | string | The participant's Placekey (e.g. `SASE1-22c-at-5sb-8hm-54v`). This is the permanent, address-bound identifier — see `README.md` for why. |
| `submitted_at` | string | ISO 8601 timestamp of submission. |
| `standing` | string | One of `public`, `participant`, `professional`, `operator`. The access state at time of submission. |
Anything beyond these common fields is addon-specific and described below.
---
## `vs01` and `dsc01` — Overwrite-by-Placekey
### Additional field
| Field | Type | Description |
|---|---|---|
| `fields` | object | Keyed object of all field values for this submission. Keys match the field IDs defined in that addon's schema files. |
### Validation rules
- `fields` must be present and non-empty.
- The orchestrator does not validate individual field values against the VS or DSC schema files — that validation already happened on the Hubzilla side before the envelope was assembled. The orchestrator's job is to store what it receives, not to re-validate business rules it does not own a copy of.
- If `fields` is missing entirely, reject with `400`. An empty or malformed envelope is a bug on the sending side and should fail loudly, not be silently dropped or partially stored.
### Storage behavior
Write (overwrite, not append) to:
```
/srv/civic-orchestrator/data/{addon}/{association_slug}/{participant_id}.json
```
The entire file is replaced with the new envelope on every write. No merge, no partial update, no history retained by the orchestrator.
### Response
```json
{ "status": "ok", "addon": "vs01", "participant_id": "SASE1-22c-at-5sb-8hm-54v", "stored_at": "2026-06-15T10:00:00+00:00" }
```
---
## `scn01` — Append-Only
### Additional fields
| Field | Type | Description |
|---|---|---|
| `pinned_scenario_ids` | array of strings | 13 scenario IDs from `scenarios.json`, deduplicated. |
| `narrative` | string | Free text. |
| `vs_snapshot` | object | The participant's full VS fields object at time of submission. Embedded, not referenced. |
| `dsc_snapshot` | object | The participant's full DSC fields object at time of submission. Embedded, not referenced. |
| `g1_tx_hash` | string or null | Optional. Transaction hash if a Ğ1 payment was made for this submission. May be `null` or absent. **Absence does not invalidate the record — see README.md.** |
### Validation rules
- `pinned_scenario_ids` must have at least 1 and at most 3 entries.
- `narrative` must be non-empty.
- `vs_snapshot` and `dsc_snapshot` must both be present. A Scenario submitted without a snapshot of the participant's current VS and DSC understanding is rejected with `400` — this is the prerequisite enforced at the envelope level. (The Hubzilla addon enforces this before the form is even shown; the orchestrator enforces it again here as the backstop, per defense-in-depth, not as the primary gate.)
- `g1_tx_hash` is never required for acceptance.
### Storage behavior
Append the complete envelope as a new entry to the array at:
```
/srv/civic-orchestrator/data/scn01/{association_slug}/records.json
```
If the file does not exist, create it with a new array containing this one entry. Never overwrite or truncate existing entries. A write that would replace rather than append is a bug and must fail loudly rather than silently destroy prior records.
### Response
```json
{ "status": "ok", "addon": "scn01", "participant_id": "SASE1-22c-at-5sb-8hm-54v", "record_index": 14, "g1_tx_hash": null, "stored_at": "2026-06-15T10:00:00+00:00" }
```
`record_index` is the zero-based position of this entry in the association's records array, returned so the Hubzilla addon can display a confirmation reference if needed.
---
## Error Responses
| Code | Condition |
|---|---|
| `401` | Missing or incorrect `X-Node-Token`. |
| `400` | Missing required common field, missing addon-specific required field, or unrecognized `addon` value. |
| `500` | Storage write failed (disk, permissions, JSON encode failure). The orchestrator must log the full envelope to a separate failure log before returning this — a failed write must never silently lose the submission. |
Every error response includes a plain-language `message` field. No stack traces, no raw exception text.
---
## What This Spec Does Not Cover
Ğ1 transaction broadcast and verification (signing happens in g1wallet, broadcast relay happens in g1wallet's spool, the orchestrator only records the resulting hash if one is provided — it does not initiate or verify the transaction itself). That flow is a separate, not-yet-built piece of work and will get its own contract addendum when it is built.
Operator manage/review read endpoints (`GET` routes for the orchestrator's stored data) are also out of scope for this version. This spec covers writes only.