Skip to content

feat(transparency): public read API, audit page, self-verifying decision documents - #67

Merged
github-actions[bot] merged 1 commit into
mainfrom
feat/solon-v1-transparency
Aug 7, 2026
Merged

feat(transparency): public read API, audit page, self-verifying decision documents#67
github-actions[bot] merged 1 commit into
mainfrom
feat/solon-v1-transparency

Conversation

@catomean

@catomean catomean commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

S4+S5 of the Solon v1 plan (S0 #64, S1+S2 #65, S3 #66) — shipped together because both are the public transparency surface. This is the integration surface OrangeCat's decision edge (OC2) consumes.

Public reads (auth-free)

  • GET /api/orgs/[slug] — organization + public member roster (addresses, public keys, weights — no secrets exist here because Solon never holds any).
  • GET /api/orgs/[slug]/proposals — proposals with session state.
  • GET /api/orgs/[slug]/policies/[key] — version history; v1 labeled bootstrap, later versions carry their approving session.
  • GET /api/orgs/[slug]/audit — the append-only audit stream (limit param, capped 500).
  • GET /api/orgs/[slug]/treasury — live on-chain balances, moved from /api/bitcoin/wallet/[orgId] to one canonical slug path.

Keystone: GET /api/v1/decisions/[sessionId]

The self-verifying decision document: proposal (contentHash + reconstructed proposer message + signature), rules snapshotted at open, every vote's exact signed message + signature + voter public key, tally, outcome. Evidence, not authority. 409 while the session is still open; a decision only exists once finalized.

The integration spec now performs the consumer's full verification loop: re-verifies the proposer signature and all three vote signatures, re-hashes proposedContent against contentHash, checks each signed message binds this session and voter, and recomputes the tally from the votes — never trusting the server's arithmetic.

Webhook rail

decision.finalized emitted on session close — HMAC X-Solon-Signature: sha256=<hex>, idempotent event_id, 3 attempts with backoff. Doorbell, not courier: the payload names the decision; the consumer fetches the document and re-verifies before acting. Env-gated (SOLON_WEBHOOK_URL/SOLON_WEBHOOK_SECRET), inert until configured. Deviation from plan noted: a DB-backed multi-endpoint rail (OC's deliveryService shape) is deferred until a second consumer exists — one env-configured consumer is the v1 reality, and the consumer's reconciliation poll heals missed deliveries.

Site

  • /governance/audit — renders the audit stream itself: no summaries, no derived metrics, nothing untraceable to a row. Added to nav.
  • Integration page lists exactly the live endpoints.

Chore

.next excluded from tsconfig — stale generated route types broke typecheck after every route move (second occurrence; class ended per never-twice).

Verification

  • npm run verify green (29 unit tests).
  • Integration spec against fresh dockerized postgres 16 (migration replay + full spine + decision-document self-verification): 2/2 green.
  • npm run build green.

🤖 Generated with Claude Code

…ying decision documents

S4+S5 of the Solon v1 plan: the integration surface consumers re-verify
rather than trust.

Public reads (auth-free — transparency is the product):
- GET /api/orgs/[slug] — organization + public member roster (addresses,
  public keys, weights; there are no secrets to hide because Solon never
  holds any)
- GET /api/orgs/[slug]/proposals — proposals with session state
- GET /api/orgs/[slug]/policies/[key] — version history; v1 labeled
  bootstrap, later versions carry their approving session id
- GET /api/orgs/[slug]/audit — append-only audit stream
- GET /api/orgs/[slug]/treasury — live on-chain balances (moved from
  /api/bitcoin/wallet/[orgId]; slug-only, one canonical path)

Keystone: GET /api/v1/decisions/[sessionId] — the self-verifying decision
document: proposal with contentHash and the exact proposer message +
signature, rules snapshotted at open, every vote's signed message +
signature + public key, tally, outcome. The integration spec now does what
OrangeCat will do: re-verifies every signature, re-hashes the content, and
recomputes the tally from the votes — never trusting the server's arithmetic.

Webhook rail: decision.finalized emitted on session close (HMAC
X-Solon-Signature: sha256=<hex>, idempotent event_id). Doorbell, not
courier — consumers fetch and re-verify the document. Env-gated
(SOLON_WEBHOOK_URL/SECRET) and inert until configured; a DB-backed
multi-endpoint rail is deferred until a second consumer exists.

Site: /governance/audit renders the audit stream itself — no summaries, no
derived metrics, nothing untraceable to a row. Nav + integration page
updated to list exactly the live endpoints.

Chore: .next excluded from tsconfig — stale generated route types broke
typecheck after every route move/deletion (second occurrence; class ended).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions
github-actions Bot merged commit c156054 into main Aug 7, 2026
2 checks passed
@github-actions
github-actions Bot deleted the feat/solon-v1-transparency branch August 7, 2026 19:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant