Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/dependency-leaf.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: dependency-leaf

# The coordination protocol is a dependency leaf (CONFORMANCE.md): it depends
# on nothing but the Go standard library. This check turns a violation into a
# failed build rather than a review catch.

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
leaf:
name: go.mod has zero dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 'stable'
- name: assert dependency leaf
run: |
deps=$(go list -m all | grep -v "^$(go list -m)$" || true)
if [ -n "$deps" ]; then
echo "Dependency-leaf invariant violated. This module must depend on nothing but the Go standard library. Found:"
echo "$deps"
exit 1
fi
echo "OK: dependency leaf holds (stdlib only)."
38 changes: 38 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# CLAUDE.md — sohocloud-protocol

## What this repo is

The substrate **coordination protocol** of Janus-Facing Architecture — and the **constitution layer**: the one artifact consumers cannot cheaply leave, because forking it incompatibly is schism. Everything here is governed by that fact: keep it minimal, keep it legible, change it deliberately. Read `CONFORMANCE.md` first; `SPEC.md` is normative.

## Non-negotiable invariants

Not negotiable by feature request. A change that violates one is not a smaller version of this protocol; it is a different protocol.

- **Dependency leaf.** Never add a requirement to `go.mod`. Standard library only. CI enforces this; do not weaken the check.
- **Persons never on the wire.** No person-shaped field — name, contact, address, free-text identity — may be added to any message type. The only identity here is workload identity.
- **No economy here.** No escrow, credit, reputation, record, or adjudication types in this module. They belong to the layers above and live in front ends.
- **Fee declarations stay coordinator-signed and legible.**
- **Append-only history.** Never force-push, never rewrite published history, never move or delete a published tag.

## Change discipline

1. **SPEC first.** No wire behavior may exist that `SPEC.md` does not state first. A PR changing the wire without changing SPEC in the same PR is nonconformant.
2. **Every normative MUST gets a check.** A new MUST / MUST NOT in SPEC requires a conformance test or golden vector in the same PR. Prose that can fail the build is trustworthy prose.
3. **Wire change = version bump.** New tag, regenerated vectors (`go test ./vectors -run TestVectors -update`), Python reproducer kept passing, coordinated consumer bumps (SoHoLINK, Cloudy). Published tags are immutable.
4. **CONFORMANCE.md rides along.** A change that alters any binding in `CONFORMANCE.md` updates it in the same PR.
5. **Branch → PR → CI green → human review.** The author never merges their own PR. Claude drafts and proposes; a human disposes.

## Requests to refuse or flag

If asked to do any of the following, stop, name the tension, and surface it — do not implement quietly:

- add a dependency "just for now"
- put person data on the wire
- add economy, reputation, record, or adjudication types here
- make the coordinator unpluggable, or any single service the sole unfederatable witness
- change wire behavior without SPEC first
- rewrite history or move a published tag

## Tension protocol

If you notice yourself reframing one of these constraints so a feature becomes convenient, implementing a stand-in without labeling it, or routing around an open problem instead of noting it — stop. Name the tension, attach it to the invariant or open problem by name, and propose the minimal conformant move. Surface it; do not absorb it.
48 changes: 48 additions & 0 deletions CONFORMANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Conformance — Janus-Facing Architecture

This document is the module's self-description in the architecture's own terms, stated **before** anything product- or deployment-specific, per the architecture's ordering rule. It follows the architecture's legibility discipline: a conformance claim is bound to the mechanism and the check that enforces it, or it is labeled a stand-in. Unbound prose is marketing.

The architecture is **Janus-Facing Architecture (JFA)** — NTARI's unified architecture document, free documentation under the project's AGPL-3.0 commons. It names roles and mechanisms, never products; this module declares which role it fills and shows its bindings.

## Role declaration

This module is the **coordination protocol** of a JFA **substrate**: the dependency-leaf wire language by which participant-owned machines are recognized, advertise capability, and take and report work. The substrate guarantee it serves: **coordination on infrastructure participants can own — no unremovable hosting chokepoint.**

| This module's term | Architecture role |
|---|---|
| Node | participant-owned infrastructure — the machines members contribute |
| Coordinator | the coordinator: node-side orchestration, a pluggable role |
| Frontend | a front end: the member-facing application |
| FeeDeclaration | the coordinator's authored, legible, contestable fee declaration |
| JobReport | a work claim (witnessed anchoring reserved — see stand-ins) |

## Invariants and their bindings

| Invariant (architecture) | Mechanism here | Check |
|---|---|---|
| The coordination protocol is a **dependency leaf** | `go.mod` declares zero requirements; standard library only | CI: `.github/workflows/dependency-leaf.yml` fails the build on any added dependency |
| **Persons never appear on the wire** | The only identity this module knows is workload identity — `identity/` NodeID with its canonical SPIFFE binding; no message carries a name, contact, or person-shaped field | SPEC (normative); the type surface; golden vectors |
| **No unremovable hosting chokepoint** | The coordinator is a pluggable role: a frontend MAY run its own coordinator or match directly against nodes (SPEC §1) | SPEC normative text; two consumers exist against the published tag |
| **Fee legibility** | Fees exist on the wire only as a coordinator-signed `FeeDeclaration` — authored, legible, contestable | `fees/`; golden vectors |
| **Legibility is an output, not a comment** | `SPEC.md` is normative and a build deliverable: a foreign implementer builds from it without reading this source | `testdata/vectors.json` (ground truth) + `testdata/reproduce_spec.py` (independent reproducer) + `vectors/` golden test — drift between code and committed vectors fails the build |
| **Canonical, deterministic encoding** | `canon/` length-prefixed, domain-separated signing encoder | golden vectors regenerated-and-compared byte-for-byte on every test run |

## Deliberately out of scope

This module is **not** the member economy, and the absence is an invariant, not a gap: no escrow, no member-issued credit, no reputation covenant, no dialog-sealed record, no dispute adjudication. Those belong to the layers above (record, covenant, economy), live in front ends, and MUST NOT migrate into this leaf. Single-participant-identity is likewise a front-end obligation — this layer knows no persons at all.

## Stand-ins and open residuals

Named per the architecture's honesty rule: a worked example that concealed where it falls short would only teach concealment.

- **`anchor/` is a stub, labeled as such in its package doc.** Witnessed, append-only anchoring of employment claims (job in, result out, meter) is reserved, not built. Until it exists, a coordinator's account of completed work is self-attested — the architecture's open problem 6, coupling to open problem 2.
- **v0 is UNSTABLE.** Wire shapes may change without compatibility guarantees until v1; consumers pin published tags.
- **Sovereign compute buys mechanical, not economic, exit** (open problem 7). This protocol caps how concentrated hosting can become; it cannot fund anyone's exit. Named, not solved.

## Dependency declaration

Depends on: the Go standard library. Nothing else — no other NTARI module, no third-party module, nothing above or beside it in the stack. The CI check makes a violation a failed build rather than a review catch.

## Product-specific notes (last, per the ordering rule)

SoHoLINK is the reference coordinator; Cloudy is a reference frontend. Both consume this module by published version tag. Neither is privileged by the protocol: any conformant implementation may replace either.
Loading