feat(consumerapi): member-facing JSON API — slice 1 (members + techtree + market)#7
Merged
Merged
Conversation
…A layers + coord client Brings the seed skeleton up to the built state: internal/economy (mutual credit, escrow/credit policy switch), internal/covenant (LBTAS distributions, harm flagging), internal/record (dialog-sealed witnessed log), internal/coord (protocol httpjson client), composition tests incl. the no-cross-layer-import tripwire. Preserves seed LICENSE. Signed-off-by: Jodson Graves <info@ntari.org>
internal/dispute: staff-adjudicated (generic Charter roster, threshold signatures), mode-aware resolution (escrow: signed escalation, moves no money; credit: reputational + voluntary payee-signed refund — forced clawback impossible by economy invariant), anchored to sealed record entries, append-only store with a permanent adjudicated-tuple guard so a resolved exchange can never be re-opened (double-refund prevention). Signed-off-by: Jodson Graves <info@ntari.org>
Drops the local ../sohocloud-protocol replace so Cloudy builds from a fresh clone and in CI (the sibling exists only on a dev machine) — mirrors the same fix already made in SoHoLINK. The protocol is public and tagged v0.1.0. Signed-off-by: Jodson Graves <info@ntari.org>
B5 — operator credential (internal/opcred): - keyset.go: 7 Ed25519 keypairs, on-disk custody (0600, O_EXCL no-overwrite), load probes + duplicate-key detection, domain-tagged keyset Hash. Private keys never leave disk. - signer.go: TransmissionSigner builds+signs a protocol OperatorTransmission with 2 distinct keys (21-pair rotation), 32-byte nonces, strictly-increasing Seq — round-trips the protocol's own Verify. - conformance.go: fresh-oracle conformance responder (wire structs byte-match the coordinator's). - cmd/operator-keygen: one-time keyset bootstrap (refuses to overwrite). B6 — root-of-trust SEAM (device later; not blocking on hardware): - root.go: RootSigner interface + canon-domain-tagged (cloudy/opcred/delegation/v0) short-lived delegations (root authorizes the online keyset; inclusive window, 30-day TTL cap, pinned-key verification); StandIn labels a file-backed root as the stand-in it is. - root_file.go: FileRootSigner (local disk); the device/HSM slot is the named follow-up. Stdlib + sohocloud-protocol only (JFA leaf discipline). Table-driven tests: signing round-trips vs protocol Verify, keygen overwrite refusal, delegation verify/expiry/mismatch, domain-separation replay proof. Signed-off-by: Jodson Graves <info@ntari.org>
… countermeasures A host storing a member's data can neither read it nor learn much from watching it move. Implements PHASE1 design 5a/5b entirely above the sohocloud-protocol thin waist; nothing here touches the wire, and the wire never carries stored content. Pipeline (5a): frame+pad to a quantized size class -> pluggable erasure Coder -> per-shard AES-256-GCM under a random per-object key, position bound as AAD, content-addressed by SHA-256. Object keys and manifests live in the member-local Locker; random ObjectIDs (never content-derived -- convergent encryption would leak plaintext equality). The four countermeasures (5b): 1. Size quantization (quantize.go, object.go): every sealed shard in a class is byte-identical in length; enforced by construction and by TestQuantizationUniformity. 2. Placement + fetch decorrelation (placement.go, fetch.go): hard distinct-host rule (fails closed on insufficient hosts, incl. the duplicate-ID starvation case), soft distinct-owner preference, entropy-shuffled placement; randomized fetch order with jittered delays over a caller-chosen window. 3. Relay boundary (relay.go): the Relay interface is the only doorway for shard bytes; no host-addressed API exists in the package, so the compiler enforces that hosts see cloudyd's address, never a member's. 4. Audits as cover traffic (audit.go, cover.go): finite-budget challenge-table proof-of-storage (single-use nonced range digests, constant-time verify) + memoryless exponential probe cadence where latency-tolerant reads ride slots -- TestCoverReadRidesSlot proves the host-observable process is identical with or without reads. Labeled stand-ins, house discipline: StandInSplitter (K data shards, ZERO parity -- Reed-Solomon is the named follow-up) and the finite challenge table (homomorphic-tag PoR is the named follow-up). The lease + audit wire messages are SCP v0.3 scope per Development/SCP-completion-roadmap.md. Stdlib only; entropy injected everywhere for deterministic tests. 21 tests, all green (full repo suite green serialized; parallel test execution on the dev box still trips WDAC flakiness -- known issue). Signed-off-by: Jodson Graves <info@ntari.org>
H1 (HIGH) — kill the AES-GCM nonce-reuse hazard. Shard nonces were derived from the shard index, safe only if one ObjectKey ever encrypts one plaintext. sealShard now draws a FRESH RANDOM 12-byte nonce per shard and prepends it (Sealed = nonce || ciphertext||tag); openShard reads it back. Re-sealing an edited object under the same key (the natural re-upload path) now draws independent nonces, so (key,nonce) never repeats — the GCM catastrophe (plaintext-XOR + auth-subkey recovery) can no longer arise even if a caller reuses a key. Quantization survives: every sealed shard in a class is still a fixed nonceLen+class+tag bytes. New TestReSealSameKeyUsesFreshNonce proves it. L3 — anchor entropy to a secure default. New randOr(): every public entry point (NewObjectKey/ID, SealObject, PlaceShards, FetchPlan, BuildChallengeTable, NewCoverScheduler) treats a nil io.Reader as crypto/rand, so a production caller gets CSPRNG randomness by default and only tests pass a (deterministic) reader. TestNilRandUsesSecureDefault covers it. L2 — replace a fictional test citation with a real tripwire. relay.go claimed test/composition asserted the package's no-network property; it did not. New TestNoNetworkImport walks and fails if storage transitively imports any net package, so a future host-dialing edit breaks the build. L7/L8 — correct overstated doc.go claims: placement guarantees 'no single HOST regroups', not 'no operator correlates' (a multi-node owner can co-observe when owners are scarce); the cover cadence is near-memoryless (clamped exponential), not strictly memoryless. L9 — document that opcred's 0700/0600 seed-file modes are POSIX-only and do NOT restrict access on Windows; a Windows deployment must ACL-restrict the keyset dir itself. Full repo suite green (go test -p 1 ./...). Signed-off-by: Jodson Graves <info@ntari.org>
The knowledge substrate behind the Economy & Education layer's empirical- claims surface, and the shared base the marketplace will ride (a product spec is a Kind product_spec claim, so buyer exchanges can cite/contest maker claims — reputation cannot be advertised into existence). Design, held to Building JFA v2 Part III: - Claim: a member's single-signed anchored assertion (inputs/method/result), closed field set — only structural facts + SHA-256 hashes in the commons, narrative stays member-local (no PII), Platform bound in (non-portable). A monologue (one seal), unlike record.Entry's dual-sealed dialog. - Reference: typed single-signed edges (builds_on/cites/contests/reproduces/ refutes) FROM one claim TO another; a contest is a NEW claim's edge, never an erasure. - Tree: append-only, hash-chained log; verify-on-insert; asserter-owns-From; builds_on kept acyclic (contest edges are not cycle-constrained); OpenLog replays and re-verifies (tamper/reorder fail closed) — record's discipline. - CitationWeight: a LEGIBLE, FORKABLE per-kind, distinct-asserter breakdown — deliberately NOT a score, and there is no ranking/collapse function. Two tripwire tests (reflect + go/ast) forbid any truth-certification or single-index symbol; ed25519 Verify is excluded (signature, not truth). Labeled stand-ins: citation weight is economically INERT until the Sybil approach (open problem 4) is settled — distinct-key is a weak mitigation, not resistance; nothing pays or ranks-for-sale on it. The log is not yet witnessed (shared record follow-up); single-writer StandIn, labeled. Imports only the protocol canon + stdlib — a leaf like the other JFA layers; the composition root wires it to covenant (rates the claimant) and record. Also adds .github/workflows/ci.yml — Cloudy had NO CI (audit gap); this new code can only truly execute in CI because Windows Application Control blocks fresh Go test binaries on the dev host (build/vet pass locally; 9/9 techtree tests verified by running the compiled binary from the project tree). Signed-off-by: Jodson Graves <info@ntari.org>
#1+#4) The manufacturer/product-listing surface (and secondary-market resale), built on internal/techtree per the Market+TechTree plan. A leaf package like the other JFA modules: imports only the protocol canon + stdlib and holds cross-layer references as opaque [32]byte values; the composition root wires it to techtree/covenant/economy. - Category: closed allowlist aligned to the Substrate node-class taxonomy — computer (A), mobile_phone/tablet (B), smart_tv/tv_streaming_stick (C), nas_storage (D). Out-of-allowlist listings are rejected (the 'no tomatoes' rule); curation is by TYPE, never pay-to-list. - Listing: maker-signed, closed field set (no free text → no PII; name/copy/ images stay member-local). Points at a techtree product_spec claim (SpecRef) so advertised specs are contestable claims, not certified facts — this is how 'reputation cannot be advertised into existence' is structural. Platform bound in (non-portable). - AcceptedRails {fiat, member_credit}: the PER-MARKET half of the two-level credit gate. Fiat works day one; member_credit is only live once the global economy Mode is ModeCredit (Sybil + regulatory gated). One sovereign currency, per-market acceptance, never mixed or converted. - Catalog: append-only, hash-chained, verify-on-insert, dedup, per-platform; RecordSale threads completed exchanges (opaque ExchangeRef) so per-product LBTAS = the covenant's distribution over SalesOf(listing), joined at the root, and a used item's prior sales are its witnessed provenance (secondary market, #4). ByCategory orders by APPEND, never rank/promotion. OpenLog replays + re-verifies (tamper/reorder fail closed). - No paid placement / no truth authority: there is deliberately no promote/sponsor/boost/rank/certify field, enforced by a reflect + go/ast tripwire test. Per-product LBTAS aggregation and the actual fiat/credit settlement are composition-root concerns (this leaf provides the product identity + sales refs; covenant/economy/payment provide distribution + settlement) — the same opaque-ref discipline dispute uses. go build/vet clean; 6/6 tests pass (run from the project tree; WDAC blocks fresh test binaries in temp — CI is the authoritative gate). Signed-off-by: Jodson Graves <info@ntari.org>
…e, market)
The first member-facing ingress the cmd/cloudy composition root said did not
exist. internal/consumerapi + cmd/cloudyd serve the React Native app's
onboarding, Technology Tree, and hardware Market surfaces (Phase-1 design §3).
Trust model (design §7.1): member keys NEVER leave the device. Every
member-authored artifact — claim, reference, listing — is signed CLIENT-SIDE
and arrives already sealed; the server VALIDATES (signature + a registered
member + invariants), stores, and routes the member-local narrative to the
erasable record.Locker, keeping only its hash in the commons. The server holds
no member private key. Reads are public (the commons carries no PII; JFA 'no
information asymmetry') so GET endpoints need no auth; writes are authenticated
by the embedded artifact signature itself, so slice 1 needs no session tokens.
Endpoints (/api/v1):
- POST /members — self-signed key-possession proof → mints AccountID+MemberID
(open, idempotent onboarding).
- GET /members/{id}/standing — LBTAS distributions per category + pooled +
harm count; never a score.
- POST /claims — ingest a client-signed techtree claim; narrative must hash to
the signed hashes, then goes to the Locker (commons keeps only the hash).
- GET /claims/{id} — structural facts + legible citation-weight breakdown +
narrative from the Locker.
- POST /references — client-signed citation/contest/reproduce edge.
- GET /market/categories — the node-class hardware allowlist.
- POST /market/listings — client-signed listing; requires the Spec to be an
anchored product_spec claim authored by the maker (specs are contestable
claims, not certified facts).
- GET /market/listings?category= and /market/listings/{id} — public browse,
append order (never ranked/promoted); per-listing sales_count = provenance
depth.
Slice 1 uses in-memory stores + ephemeral platform like cmd/cloudy — honest
about not being durable. Deferred slices: economy spends/settlement, disputes,
contribution control, storage, and session-token auth.
Verified: full go build/vet clean; 4/4 httptest cases pass (end-to-end
register→anchor spec→reproduce→citation-weight→list→browse→standing, plus
wrong-key-register, unregistered-claimant, and narrative-hash-mismatch
rejections); live cloudyd smoke test (categories 200, missing claim 404,
bad register 400). Tests run from the project tree (WDAC blocks temp-dir test
binaries); Cloudy CI (added in PR #5) is the authoritative gate.
Signed-off-by: Jodson Graves <info@ntari.org>
NetworkTheoryAppliedResearchInstitute
changed the base branch from
feat/market
to
main
July 13, 2026 10:43
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The first member-facing ingress — what the React Native app will talk to, and the surface the headless
cmd/cloudyroot said didn't exist yet. Stacked onfeat/market(PR #6).Trust model (Phase-1 design §7.1)
Member keys never leave the device. Every member artifact (claim, reference, listing) is signed client-side and arrives sealed; the server validates (signature + registered member + invariants), stores, and routes member-local narrative to the erasable
record.Locker, keeping only its hash in the commons. The server holds no member private key. Reads are public (commons has no PII); writes are authenticated by the embedded artifact signature, so slice 1 needs no session tokens.Endpoints (
/api/v1)POST /members— self-signed key-possession proof → AccountID + MemberID (open, idempotent).GET /members/{id}/standing— LBTAS distributions (per-category + overall + harm), never a score.POST /claims— client-signed techtree claim; narrative must hash to the signed hashes, then → Locker.GET /claims/{id}— structural facts + legible citation-weight breakdown + narrative from the Locker.POST /references— client-signed cite/contest/reproduce/refute/builds_on edge.GET /market/categories— the node-class hardware allowlist.POST /market/listings— client-signed listing; Spec must be an anchoredproduct_specclaim authored by the maker (specs are contestable claims, not certified facts).GET /market/listings?category=+/market/listings/{id}— public browse, append order (never ranked/promoted);sales_count= provenance depth.Plus
cmd/cloudyd— the serving daemon.Verification
go build/go vetclean; 4/4 httptest cases pass — full end-to-end (register → anchor product_spec → buyer reproduces it → citation weight updates → list → browse → standing) plus wrong-key-register (401), unregistered-claimant (401), and narrative-hash-mismatch (400) rejections. Livecloudydsmoke test: categories 200, missing claim 404, bad register 400. (Tests run from the project tree — WDAC blocks temp-dir binaries; Cloudy CI from PR #5 is the authoritative gate.)Deferred slices
Economy spends/settlement (fiat + member-credit rails), disputes, contribution control, storage, session-token auth, and the per-product LBTAS join (needs the exchange-ingress that records sales).
🤖 Generated with Claude Code