S05: NATS endpoint catalog, passthrough and error-bundle docs
This commit is contained in:
commit
0cf87863d4
15 changed files with 1315 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
build/
|
||||
__pycache__/
|
||||
7
LICENSE
Normal file
7
LICENSE
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
Copyright 2026 Verae / George Lambert
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files to use, copy, modify,
|
||||
merge, publish, and distribute, subject to including this notice.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
||||
11
Makefile
Normal file
11
Makefile
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
.PHONY: html latexpdf all
|
||||
html:
|
||||
$(SPHINXBUILD) -b html "$(SOURCEDIR)" "$(BUILDDIR)/html"
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex "$(SOURCEDIR)" "$(BUILDDIR)/latex"
|
||||
cd "$(BUILDDIR)/latex" && pdflatex -interaction=nonstopmode nats-service-endpoints.tex && pdflatex -interaction=nonstopmode nats-service-endpoints.tex
|
||||
all: html latexpdf
|
||||
15
README.md
Normal file
15
README.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# nats-service-endpoints
|
||||
|
||||
Canonical list of NATS subjects for Verae / PFC / secure-messaging.
|
||||
|
||||
* `catalog/endpoints.json` — secure-messaging + admin-config endpoints (full records)
|
||||
* `catalog/pfc-derived.json` — imported from peergos-for-compliance `subjects.json`
|
||||
* `catalog/schema.json` — JSON Schema for an endpoint row
|
||||
* Docs: `source/` (Sphinx HTML + LaTeX PDF)
|
||||
|
||||
Hub: https://git.georgelambert.org/marchon/system-git-sync
|
||||
|
||||
Passthrough: the **destination** (and NATS subject) may be in the clear; the
|
||||
**body** is ciphertext. The broker is untrusted.
|
||||
|
||||
Not a HIPAA/SOC 2/ISO certificate.
|
||||
93
catalog/endpoints.json
Normal file
93
catalog/endpoints.json
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"version": "1.0.0",
|
||||
"broker": "untrusted",
|
||||
"git": "https://git.georgelambert.org/marchon/nats-service-endpoints",
|
||||
"related": [
|
||||
"https://git.georgelambert.org/marchon/secure-messaging",
|
||||
"https://git.georgelambert.org/marchon/peergos-for-compliance",
|
||||
"https://git.georgelambert.org/marchon/system-git-sync"
|
||||
],
|
||||
"encryption_modes": {
|
||||
"npe": "HPKE X25519-HKKE-SHA256-ChaCha20-Poly1305; dest inbox npe.inbox.<id_fp>",
|
||||
"lab-xor": "pfc-lab-xor PSK; lab only",
|
||||
"plain-lab": "JSON body; lab tests only",
|
||||
"passthrough": "destination and NATS subject in the clear; payload ciphertext"
|
||||
},
|
||||
"endpoints": [
|
||||
{
|
||||
"name": "verae.sm.send",
|
||||
"pattern": "core",
|
||||
"for": "Deliver a passthrough secure message to a mailbox identity",
|
||||
"from": ["console", "go-leaf", "access-api"],
|
||||
"to": ["sm-leaf", "npe sidecar"],
|
||||
"in": {
|
||||
"header": ["to", "from_lookup_id", "alg", "error_token"],
|
||||
"body": "ciphertext (not readable by broker or sender after send)"
|
||||
},
|
||||
"out": {"ack": {"lookup_id": "string", "accepted": true}},
|
||||
"encryption": "passthrough-full-body",
|
||||
"reject": ["missing to", "unsigned config", "alg not in signed config", "empty ciphertext when mode!=plain-lab"],
|
||||
"dead": "verae.sm.dead"
|
||||
},
|
||||
{
|
||||
"name": "verae.sm.dead",
|
||||
"pattern": "core",
|
||||
"for": "Dead-letter failed service requests",
|
||||
"from": ["sm-leaf"],
|
||||
"to": ["error-handler"],
|
||||
"in": {"header": ["error_code", "lookup_id", "to"], "body": "Network Error Bundle"},
|
||||
"out": "none",
|
||||
"encryption": "system-key bundle plus sender-only status",
|
||||
"reject": ["bundle missing ct_system"],
|
||||
"dead": "none"
|
||||
},
|
||||
{
|
||||
"name": "verae.sm.error",
|
||||
"pattern": "core",
|
||||
"for": "Build and emit a Network Error Bundle",
|
||||
"from": ["sm-leaf", "pfc-admin"],
|
||||
"to": ["sender (ct_sender)", "system logger (ct_system)"],
|
||||
"in": {"error_code": "string", "lookup_id": "string", "dest_class": "mailbox|service"},
|
||||
"out": {"ct_sender": "box to sender", "ct_system": "box to system public key"},
|
||||
"encryption": "no recipient payload; bounce metadata only",
|
||||
"reject": ["unknown error_code"],
|
||||
"dead": "verae.sm.log.summary"
|
||||
},
|
||||
{
|
||||
"name": "verae.sm.log.summary",
|
||||
"pattern": "core",
|
||||
"for": "Central log summaries of system failures (no message bodies)",
|
||||
"from": ["sm-leaf", "pfc-admin"],
|
||||
"to": ["central-log"],
|
||||
"in": {"code": "string", "lookup_id": "string", "dest_class": "string"},
|
||||
"out": "ack",
|
||||
"encryption": "summary plaintext allowed; never ciphertext of mail",
|
||||
"reject": ["body-like fields present"],
|
||||
"dead": "none"
|
||||
},
|
||||
{
|
||||
"name": "verae.admin.config.sign",
|
||||
"pattern": "core",
|
||||
"for": "Sign a new configuration payload; reject unsigned files at load",
|
||||
"from": ["pfc-console", "pfc-config TUI"],
|
||||
"to": ["admin-history cube"],
|
||||
"in": {"payload": "object", "prev_signed": "object|null"},
|
||||
"out": "signed wrapper",
|
||||
"encryption": "Ed25519 signature; payload may be lab-clear",
|
||||
"reject": ["bad key_id", "canonical JSON mismatch"],
|
||||
"dead": "verae.sm.dead"
|
||||
},
|
||||
{
|
||||
"name": "verae.admin.history.append",
|
||||
"pattern": "core",
|
||||
"for": "Append prev file, new file, and unified diff to DataCube admin-history",
|
||||
"from": ["verae.admin.config.sign"],
|
||||
"to": ["pfc CubeStore kind=admin-history"],
|
||||
"in": {"prev": "string", "new": "string", "diff": "string", "actor": "string"},
|
||||
"out": "chain row",
|
||||
"encryption": "chain hashes; not NPE (admin audit)",
|
||||
"reject": ["empty actor"],
|
||||
"dead": "none"
|
||||
}
|
||||
]
|
||||
}
|
||||
1001
catalog/pfc-derived.json
Normal file
1001
catalog/pfc-derived.json
Normal file
File diff suppressed because it is too large
Load diff
28
catalog/schema.json
Normal file
28
catalog/schema.json
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "NATS-Service-Endpoints catalog",
|
||||
"type": "object",
|
||||
"required": ["version", "endpoints"],
|
||||
"properties": {
|
||||
"version": {"type": "string"},
|
||||
"endpoints": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["name", "for", "from", "to", "in", "out", "encryption", "reject"],
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"pattern": {"type": "string"},
|
||||
"for": {"type": "string"},
|
||||
"from": {"type": "array", "items": {"type": "string"}},
|
||||
"to": {"oneOf": [{"type": "string"}, {"type": "array", "items": {"type": "string"}}]},
|
||||
"in": {},
|
||||
"out": {},
|
||||
"encryption": {"type": "string"},
|
||||
"reject": {"type": "array", "items": {"type": "string"}},
|
||||
"dead": {"type": "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
scripts/import_pfc_subjects.py
Normal file
38
scripts/import_pfc_subjects.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Merge peergos-for-compliance subjects.json into catalog/pfc-derived.json."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
PFC = Path("/Users/marchon/research/peergos-for-compliance/schemas/subjects.json")
|
||||
OUT = ROOT / "catalog" / "pfc-derived.json"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
src = json.loads(PFC.read_text())
|
||||
rows = []
|
||||
for s in src.get("subjects") or []:
|
||||
rows.append(
|
||||
{
|
||||
"name": s["name"],
|
||||
"pattern": s.get("pattern"),
|
||||
"for": "PFC/Verae subject (see peergos-for-compliance/docs/nats-addresses.md)",
|
||||
"from": ["pfc services", "connectors"],
|
||||
"to": [s.get("owner") or "unknown"],
|
||||
"in": {"fields": s.get("payload") or []},
|
||||
"out": "service-specific",
|
||||
"encryption": "NATS envelope pfc-lab-xor or NPE sidecar; Pattern A apps never publish",
|
||||
"reject": ["unknown subject", "missing required fields"],
|
||||
"dead": "none",
|
||||
"git": "https://git.georgelambert.org/marchon/peergos-for-compliance",
|
||||
}
|
||||
)
|
||||
OUT.write_text(json.dumps({"version": src.get("version"), "endpoints": rows}, indent=2) + "\n")
|
||||
print("wrote", OUT, "n=", len(rows))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
10
source/conf.py
Normal file
10
source/conf.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
project = "NATS Service Endpoints"
|
||||
copyright = "2026, Verae"
|
||||
author = "Verae / George Lambert"
|
||||
extensions = []
|
||||
html_theme = "alabaster"
|
||||
latex_engine = "pdflatex"
|
||||
latex_documents = [
|
||||
("index", "nats-service-endpoints.tex", "NATS Service Endpoints", "Verae", "manual"),
|
||||
]
|
||||
latex_elements = {"papersize": "letterpaper", "pointsize": "11pt"}
|
||||
18
source/config.rst
Normal file
18
source/config.rst
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
Signed configuration
|
||||
====================
|
||||
|
||||
A config file is unusable unless it is an Ed25519 wrapper::
|
||||
|
||||
{ "payload": { ... }, "sig": { "alg": "ed25519", "key_id": "...", "signature": "hex" } }
|
||||
|
||||
Select algorithms inside **payload** (the signed object):
|
||||
|
||||
* ``crypto.mode``: ``npe`` | ``lab-xor`` | ``plain-lab``
|
||||
* ``routing.mode``: ``passthrough``
|
||||
* ``admin.history_cube``: cube id for admin-history
|
||||
* ``crypto.system_key_id``: system public key id
|
||||
* ``logging.mode``: ``summary``
|
||||
|
||||
After an admin console change: sign again, then append previous file, new
|
||||
file, and unified diff to the admin-history DataCube
|
||||
(``verae.admin.history.append``).
|
||||
17
source/endpoints.rst
Normal file
17
source/endpoints.rst
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
Endpoint records
|
||||
================
|
||||
|
||||
Full JSON: ``catalog/endpoints.json``. PFC import:
|
||||
``catalog/pfc-derived.json`` from
|
||||
https://git.georgelambert.org/marchon/peergos-for-compliance/src/branch/main/schemas/subjects.json
|
||||
|
||||
Secure-messaging subjects
|
||||
-------------------------
|
||||
|
||||
* ``verae.sm.send`` -- passthrough deliver; from console/leaf; reject unsigned
|
||||
config and empty ciphertext (unless ``plain-lab``).
|
||||
* ``verae.sm.dead`` -- dead letter; Network Error Bundle in body.
|
||||
* ``verae.sm.error`` -- emit bundle: ``ct_sender`` + ``ct_system``.
|
||||
* ``verae.sm.log.summary`` -- central summaries (codes, lookup_id, dest_class).
|
||||
* ``verae.admin.config.sign`` -- Ed25519 wrap; unsigned files rejected.
|
||||
* ``verae.admin.history.append`` -- prev, new, unified diff on DataCube chain.
|
||||
13
source/errors.rst
Normal file
13
source/errors.rst
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
Errors and Network Error Bundle
|
||||
================================
|
||||
|
||||
Never put the intended recipient payload in an error object.
|
||||
|
||||
* **ct_sender** -- failure status encrypted so **only the sender** can read it.
|
||||
* **ct_system** -- bounce report encrypted to the **system public key** for
|
||||
logging/recovery (lookup_id, error_code, dest_class, respond flag).
|
||||
* **Dead letter** -- ``verae.sm.dead`` with the same bundle.
|
||||
* **Central log** -- ``verae.sm.log.summary``: codes only.
|
||||
|
||||
The sender identifies the original message with ``lookup_id``, not by
|
||||
decrypting what they sent.
|
||||
29
source/index.rst
Normal file
29
source/index.rst
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
NATS Service Endpoints
|
||||
======================
|
||||
|
||||
Canonical catalog for Verae / PFC / secure-messaging on
|
||||
https://git.georgelambert.org/marchon/nats-service-endpoints
|
||||
|
||||
The NATS broker is **untrusted**. Production E2E is NPE (HPKE). Lab may use
|
||||
``pfc-lab-xor``. This is **not** a HIPAA/SOC 2/ISO certificate.
|
||||
|
||||
Related: `system-git-sync <https://git.georgelambert.org/marchon/system-git-sync>`_,
|
||||
`secure-messaging <https://git.georgelambert.org/marchon/secure-messaging>`_,
|
||||
`peergos-for-compliance <https://git.georgelambert.org/marchon/peergos-for-compliance>`_.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
passthrough
|
||||
endpoints
|
||||
errors
|
||||
config
|
||||
tracing
|
||||
|
||||
Passthrough
|
||||
-----------
|
||||
|
||||
Destination mailbox id and the NATS subject may be in the clear so routers
|
||||
can work. The **body** is ciphertext. After send, the sender cannot open
|
||||
that ciphertext; they keep a ``lookup_id`` only. The same rule applies to
|
||||
the responder.
|
||||
15
source/passthrough.rst
Normal file
15
source/passthrough.rst
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
Passthrough routing
|
||||
===================
|
||||
|
||||
::
|
||||
|
||||
sender --(ct, to, lookup_id)--> NATS subject
|
||||
| |
|
||||
| v
|
||||
| sm-leaf / NPE
|
||||
| |
|
||||
+---- lookup_id only ---- v
|
||||
recipient mailbox
|
||||
|
||||
Header (clear): ``to``, ``from_lookup_id``, ``alg``, ``error_token``.
|
||||
Body: ciphertext. Broker must not log ``ct``.
|
||||
18
source/tracing.rst
Normal file
18
source/tracing.rst
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
Message tracing
|
||||
===============
|
||||
|
||||
Happy path::
|
||||
|
||||
1. Load signed config (reject if unsigned).
|
||||
2. seal(body) -> envelope with to + ct + lookup_id.
|
||||
3. Publish verae.sm.send.
|
||||
4. Leaf acks {accepted:true, lookup_id}.
|
||||
5. Recipient opens ct with NPE (prod) or lab-xor (lab).
|
||||
|
||||
Failure path::
|
||||
|
||||
1. Leaf cannot deliver.
|
||||
2. Build Network Error Bundle (no mail body).
|
||||
3. Publish verae.sm.dead and verae.sm.error.
|
||||
4. Sender opens ct_sender; ops opens ct_system.
|
||||
5. Summary to verae.sm.log.summary.
|
||||
Loading…
Add table
Add a link
Reference in a new issue