Repo moved: https://codeberg.org/eparty/party-web
A messaging‑first “Party Circle” kernel for civic and political organizations that want deliberation and representation without turning identity into a surveillance problem.
This repository is a Node.js SSR application that starts as a usable discussion + notification network, then lets you progressively enable higher‑stakes modules (petitions, votes, delegation, federation) as your policy and governance mature. The identity layer is designed around blinded uniqueness: the server can enforce one natural person = one voice without storing raw PII.
Status / intent Practical scaffold for pilots, internal deployments, and research‑grade iteration. Some protocols (OIDC4VP, ActivityPub, federation hardening) are present as scaffolding and will evolve — see
ROADMAP.md.
Most “civic platforms” fail in one of two ways:
- They ship “voting” before shipping “conversation.” People need a place to learn, argue, and revise ideas before any decision has legitimacy.
- They bind trust to centralized identity. That creates pressure to collect personal data, which becomes a liability and a power imbalance.
This project takes the opposite stance:
- Start with messaging and accountability cues.
- Add formal decision tools only when you can defend them socially and operationally.
- Keep identity privacy‑first by storing only blinded hashes (not names, not documents).
- Parties, civic committees, associations, unions, NGOs, and community groups
- Researchers prototyping representation models (liquid delegation, topic‑scoped voting, federation)
- Teams that need a deployable baseline today, not a perfect protocol paper
- user: the default actor in general deployments.
- person: a Circle label for a verified natural person (civic/party mode).
When civic mode is enabled, the exclusion principle applies: org/bot/service accounts cannot hold handles or act.
The code keeps this distinction explicit so deployments can start lightweight and become stricter later without rewiring the UX. UI copy follows the same rule: “person” labels appear only when Circle enforcement is strict; otherwise the UI uses “user”.
- Messaging‑first kernel: discussion + forum + notifications work alone.
- Natural‑person exclusion principle (optional): a Circle can require verified natural persons (no org/bot/service accounts holding handles).
- Blinded uniqueness ledger: prevent duplicate participation without retaining raw PID/PII.
- Petitions → votes pipeline: proposals with evidence summaries + source links, collaborative revisions + version history, revision diffs + pre‑vote freeze, signatures/quorum, deliberation feed with stance tags (support/concern/question) + fact‑check flags, vote envelopes (signable).
- Topic gardener review: admin confirms rename/merge/split plus anchor promotion/archival suggestions with history diff previews.
- Audit trails: append‑only transactions for discussions, petitions (signatures/comments), social, group actions, and outbound deliveries with exportable summaries.
- Identity throttles: per‑handle/session rate limiting (IP fallback) to curb spam without CAPTCHA.
- Liquid representation: topic‑scoped delegation with revocable overrides.
- Federation scaffolding + redundancy knobs: gossip endpoints, signed envelopes, and ActivityPub inbox ingestion (preview‑gated) for auditability.
- Peer ledger audits: peer health tracks last ledger hash snapshots (match/mismatch) in
/adminand/health. - Storage‑agnostic: pluggable persistence adapters and
DATA_MODEprofiles. - Profile attributes: schema‑driven provider‑local fields with a per‑session editor, schema versioning, inline validation, and stale‑schema warnings; never gossiped.
- Extensions: tighten policy without forking (
src/modules/extensions/,CIRCLE_EXTENSIONS).
- Handles, roles, moderation flags, notifications
- SSR pages + partial‑HTML navigation (fast, indexable, minimal JS)
- OIDC4VP verification scaffold (EUDI wallet flow)
- Store only blinded identity hashes for uniqueness + session roles
- Petitions, collaborative drafts, and signatures (quorum gates)
- Signed vote envelopes and exports
- Topic‑scoped delegation and group‑level recommendations
- Peer gossip endpoints and quarantine hooks (stubbed)
Prereqs: Node.js 20+ (ESM) and npm. sqlite3 is bundled for the optional SQLite adapter; MySQL and MongoDB adapters require optional mysql2 and mongodb packages.
Copy .env.example to .env for the full list of available configuration variables. The server loads .env automatically at startup.
npm install
# Ephemeral dev (in‑memory)
DATA_ADAPTER=memory DATA_MODE=centralized npm start
# Persisted JSON (default)
npm start
# Standalone full-feature dev (admin/admin bootstrap)
./run-standalone.shThe standalone script seeds a local KV store, generates signing keys if missing, and prints a one-click admin login link (user: admin, password: admin).
# Messaging UI without Circle enforcement
DATA_MODE=centralized DATA_ADAPTER=memory ENFORCE_CIRCLE=false npm start
# Verified Circle with strict validation + signing
CIRCLE_PRIVATE_KEY=./priv.pem CIRCLE_PUBLIC_KEY=./pub.pem DATA_VALIDATION_LEVEL=strict ENFORCE_CIRCLE=true npm start# Tests + adapter report
npm test
npm run test:ui # UI-only suite (Puppeteer)
npm run db:checknpm testruns the fullnode:testsuite (including Puppeteer UI flows and P2P ring smoke tests) and spins up local servers on ad hoc ports with temp data files.npm run test:uiruns the UI‑only Puppeteer flow for a stable CI entry point.- If headless Chrome fails in CI, set up the system dependencies or skip the UI suite.
- External adapter tests are opt-in: set
TEST_MYSQL_URLorTEST_MONGO_URL(plus optionalTEST_MYSQL_TABLE/TEST_MONGO_COLLECTION) to exercise MySQL/MongoDB adapters.
- Server:
http://0.0.0.0:3000 - JSON persistence (default): writes under
src/data/
/landing,/healthmetrics/auth/eudistart verification,/auth/callbackreturn/discussion,/forum,/notifications,/delegation/profile(provider‑local profile attributes editor)/petitionsand/petitions/*(when enabled),/votes/*(when enabled)/social/*micro‑posts, follows, and media uploads (/social/media/{id},/social/media/report)/adminsettings, policy toggles, rate‑limit overrides, and audit summaries;/extensionsextension toggles/transactions,/transactions/export,/transactions/ledger,/transactions/gossipfor local audit + cross‑provider reconciliation/circle/*gossip/ledger/peers (federation scaffolding),/ap/*ActivityPub endpoints (/ap/actors/{hash},/ap/actors/{hash}/outbox,/ap/outbox,/ap/objects/{id},/ap/inboxingesting preview social notes for local recipients)
- One person = one voice via blinded PID hashes and policy gates.
- Soft‑power accountability: auditable policy decisions and signed vote envelopes instead of imperative mandates.
- Liquid representation: topic‑scoped delegation with revocable overrides.
- Personalizable structure manager: canonical profile fields (handle + credential/wallet binding, role/banned flag, blinded identity) are fixed across a party ring; provider‑local optional fields (contact email, personal details, notification preferences) live in a versioned schema/data‑table editor with a per‑session profile page and never gossip.
- Messaging‑first adoption: discussion and notifications work alone; petitions/votes/delegation/federation are opt‑in.
- Federation resilience: peers gossip ledger/vote hashes, reject policy id/version mismatches, and can quarantine toxic providers (peer health visible in
/admin); ActivityPub actors expose public presence. - Human‑ready maintenance: small modules, plain SSR templates, env‑driven knobs, documented flows.
- Discussion + forum: threaded posts/comments with SSR UI.
- Notifications: internal read/unread registry and preferences.
- Profile attributes: schema‑driven provider‑local fields with self‑service editing under
/profile, schema versioning, and inline validation. - Social feed: micro‑post lane with typed follows (circle/interest/info/alerts) kept separate from petitions/votes.
- Petitions: draft proposals, collaborative revisions with history + diffs, signatures/quorum, stage transitions, pre‑vote freeze, stance-tagged discussion feed.
- Votes: one vote per person when enabled; exports + envelope signing/verification when keys are configured.
- Delegation: topic‑scoped delegation with conflict resolution UI.
- Groups/elections: group‑level delegate preferences and elections (advisory by design, with optional conflict prompts); in
electionMode=vote, recommendations prefer the latest closed election winner. - Topics & gardener: dynamic topic labeling with admin review (rename/merge/split + anchor promotion/archival suggestions).
- Uniqueness ledger + gossip: exchange ledger hashes and peer hints.
- ActivityPub scaffolding: actors + outbox plus inbox ingestion that stores inbound notes as preview entries when allowed.
The verifier flow is designed to be privacy‑preserving: the server stores only a blinded hash derived from the wallet‑presented identifier plus a server salt.
sequenceDiagram
actor Person
participant Browser
participant Server
participant Wallet as Wallet (OIDC4VP / EUDI)
participant Ledger
Browser->>Server: GET /auth/eudi
Server-->>Browser: Offer deep link + QR (sessionId + salt)
Person->>Wallet: Open link / scan QR
Wallet-->>Server: OIDC4VP response (PID proof)
Server->>Server: hash(pid + salt) -> blindedPid
Server->>Ledger: Persist blindedPid in sessions + uniqueness ledger
Server-->>Browser: Set session cookie (handle, role, banned flag)
Note over Server,Ledger: Only blinded hashes are stored (no raw PID/PII)
Important: this repo provides the verification scaffold and policy hooks. Compliance and production hardening depend on your deployment’s legal and security context — treat the identity layer as a component you audit and evolve.
sequenceDiagram
participant ProviderA as Provider A
participant ProviderB as Provider B
participant Store as Ledger/Vote store
ProviderA->>ProviderB: POST /circle/gossip {ledgerHashes, peers}
ProviderB->>Store: Validate + merge uniqueness entries
ProviderB-->>ProviderA: Ack + peer hints
ProviderA->>ProviderB: POST /votes/gossip {signedEnvelopes}
ProviderB->>ProviderB: Verify signature (CIRCLE_PUBLIC_KEY) + policy id/version
ProviderB->>Store: Persist vote envelopes, reject duplicates/replays
Note over ProviderA,ProviderB: Envelopes are signed with CIRCLE_PRIVATE_KEY (ISSUER tags source)
The persistence layer is intentionally storage‑agnostic.
json(default)memory(ephemeral)sql(SQLite)kv(single‑file KV JSON)mysql(MySQL, requiresmysql2)mongodb(MongoDB, requiresmongodb)
DATA_MODE=centralized: single provider store, no gossip writes/ingestDATA_MODE=hybrid: canonical store + gossip as redundancy/auditDATA_MODE=p2p: gossip‑ledger primary, optional local cache
Two switches gate uncertified data:
DATA_VALIDATION_LEVEL(strict|observe|off)DATA_PREVIEW(true|false) to store and/or render preview entries
This allows deployments to experiment with helpers (topic gardeners, classification) and federation inputs without silently treating them as authoritative.
Ledger exports (/votes/ledger, /transactions/ledger) respect the same preview gating and omit preview entries when DATA_PREVIEW=false.
- Server:
HOST,PORT - Circle/policy:
ENFORCE_CIRCLE,CIRCLE_POLICY_ID,CIRCLE_ISSUER,GOSSIP_INTERVAL_SECONDS(issuer labels gossip envelopes;/admincan override it in settings) - Topic gardener:
TOPIC_GARDENER_SYNC_SECONDS(poll/operationsfor rename/merge/split suggestions) - Extensions:
CIRCLE_EXTENSIONS(comma‑separated module names undersrc/modules/extensions/) - Signing:
CIRCLE_PRIVATE_KEY,CIRCLE_PUBLIC_KEY(PEM) - Outbound delivery:
OUTBOUND_EMAIL_WEBHOOK,OUTBOUND_SMS_WEBHOOK,OUTBOUND_WEBHOOK_HEADERS,OUTBOUND_TRANSPORT_MODULE - Ops metrics:
METRICS_SNAPSHOT_INTERVAL_SECONDS(rolling bucket size),METRICS_SNAPSHOT_RETENTION_HOURS(retention window) - Persistence:
DATA_MODE(centralized|hybrid|p2p)DATA_ADAPTER(json|memory|sql|kv|mysql|mongodb)DATA_VALIDATION_LEVEL(strict|observe|off)DATA_PREVIEW(true|false)- SQL:
DATA_SQLITE_URL/DATA_SQLITE_FILE - KV:
DATA_KV_FILE - MySQL:
DATA_MYSQL_URL(include the database in the URL) orDATA_MYSQL_HOST+DATA_MYSQL_USER+DATA_MYSQL_DATABASE(optionalDATA_MYSQL_PORT,DATA_MYSQL_PASSWORD,DATA_MYSQL_TABLE) - MongoDB:
DATA_MONGO_URL(include the database or setDATA_MONGO_DB, plus optionalDATA_MONGO_COLLECTION)
- Rate limits: per‑action overrides via
/admin(stored insettings.rateLimits) - Social/media:
SOCIAL_MEDIA_MAX_BYTES(default 10MB),SOCIAL_MEDIA_REPORT_THRESHOLD(default 3)
Provider settings are persisted in src/data/settings.json and can be edited via /admin.
- Local messaging sandbox (no verification)
DATA_MODE=centralized DATA_ADAPTER=memory ENFORCE_CIRCLE=false— exercise UI flows without writing to disk. - Verified Circle, single provider
DATA_MODE=centralized DATA_ADAPTER=json ENFORCE_CIRCLE=true CIRCLE_POLICY_ID=party-circle CIRCLE_PUBLIC_KEY=/path/pub.pem CIRCLE_PRIVATE_KEY=/path/priv.pem— strict gates + signed vote envelopes and ledger exports. - Hardened policy via extension
AddCIRCLE_EXTENSIONS=sample-policy-tighten(or a custom module undersrc/modules/extensions/) to layer org‑specific checks without forking core code; toggles are also exposed in/extensionsand/admin. - Hybrid redundancy (canonical + gossip)
DATA_MODE=hybrid DATA_ADAPTER=sql DATA_SQLITE_FILE=./data/party.db DATA_VALIDATION_LEVEL=observe GOSSIP_INTERVAL_SECONDS=30— keep a SQL canonical store while ingesting gossip as preview data until validated. - P2P‑first ring
DATA_MODE=p2p DATA_ADAPTER=kv DATA_KV_FILE=./data/ring.json DATA_VALIDATION_LEVEL=strict DATA_PREVIEW=false CIRCLE_PUBLIC_KEY=/path/pub.pem— gossip‑first sync and only accept signed/validated envelopes. - Topic/delegation experiments
DATA_PREVIEW=true DATA_VALIDATION_LEVEL=observe— allow preview entries from topic gardeners or classification helpers; UI marks previews and replication filters them when disabled.
src/index.js,src/app/server.js,src/app/router.js
- Controllers:
src/interfaces/http/controllers/ - View helpers:
src/interfaces/http/views/
src/modules/for identity/auth, circle policy, messaging/notifications, social feed/follows, topics/classification, petitions/signatures, votes/envelopes, delegation, groups/elections, federation (ActivityPub + gossip sync helpers), and extensions.
- Adapters + migrations:
src/infra/persistence/ - Replication helpers:
src/modules/federation/replication.js - Worker hooks reserved under:
src/infra/workers/ - Shared utilities:
src/shared/utils/
- SSR templates:
src/public/templates/ - Styles/JS:
src/public/app.css,src/public/app.js
- Registry:
src/modules/extensions/registry.js - Sample:
sample-policy-tighten.js(enable viaCIRCLE_EXTENSIONS)
npm testruns thenode:testsuite intests/(hashing, migrations, policy gates, classification, extensions, Puppeteer UI flows, ring gossip).npm run test:uiruns the UI‑only flow.
- Auth & sessions:
/auth/eudiissues offers;/auth/callbackfinalizes throughsrc/modules/identity/*(blinded PID hash + ActivityPub actor) andsrc/interfaces/http/controllers/auth.js. Pending sessions can be resumed with?session={id}. - Uniqueness Ledger + circle sync: ledger/sessions/peers persisted in
src/data/;/circle/gossipingests peer hashes,/circle/ledgerexports,/circle/peersmanages hints. Signing/verification lives insrc/modules/circle/federation.js. - Policy gates: role-aware checks (person/moderator/delegate + banned flag) in
src/modules/circle/policy.js; surfaced in/healthand UI. Extensions can decorate/override decisions. - Discussion + forum:
/discussionand/forumcontrollers render SSR pages; posts/comments are persisted viasrc/infra/persistence/storage.js; topic classification hook runs per post viasrc/modules/topics/classification.js. - Social feed & follows:
/social/*handles short posts + replies + reshares driven by typed follows (circle/interest/info/alerts), plus optional photo/video uploads. Media stays provider-local, locks by default until viewers request access, and can be blocked after mass reports or manual review. Follow edges and micro-posts persist throughsrc/infra/persistence/storage.js; the feed filter surfaces per-type counts so users can see coverage at a glance, and UX keeps this lane scoped to small talk/info so petitions/votes remain distinct. - Petitions + votes:
/petitionsdrafts proposals with summary + optional full text + evidence summary/links;/petitions/updaterecords collaborative revisions and version history; quorum moves proposals into discussion (or straight to vote when configured), and/petitions/statusadvances them to vote/closed. Signatures at/petitions/sign, discussion notes at/petitions/comment(stance tags + fact‑check requests), and/petitions/voterecords one vote per person and emits a signed vote envelope (if signing keys set) fromsrc/modules/votes/voteEnvelope.js;/votes/ledgerexports,/votes/gossipingests envelopes. The proposals page includes a discussion feed and stage filter with per-proposal anchors to jump between feed items and the list. - Delegation & groups:
src/modules/delegation/delegation.jsstores per-topic delegates with auto-resolution;/delegationmanages manual preferences and/delegation/conflictprompts when cachets clash.src/modules/groups/*publishes delegate preferences and runs elections with optional second/third-choice ballots and multi-round transfers; group policy (priority vs vote, conflict rules) is stored independently, vote-mode recommendations prefer the latest closed election winner, and the delegation UI surfaces election-winner metadata. - Groups UI: closed elections surface the winner, method, and round count directly in the group panel, and closing an election auto-updates the delegate cachet for that topic.
- Topics & classification:
src/modules/topics/classification.jsroutes to extensions and the topic gardener helper (seeprinciple-docs/DynamicTopicCategorization.md) to keep categories coherent, merge/split, and avoid conflicting provider labels. A topic registry persists ids/path metadata so SSR views can render breadcrumbs. Configure anchors/pins + optional helper URL via/admin; the helper runs fromsrc/infra/workers/topic-gardener/server.pywith scheduled refactor operations, and/adminsurfaces pending rename/merge/split suggestions plus topic history diffs for review. - Notifications:
/notificationslists unread;/notifications/readmarks them;/notifications/preferencesstores per-user proposal comment alert preferences; backing store handled bysrc/modules/messaging/notifications.js. - Admin & settings:
/admintoggles Circle policy, verification requirement, issuer (public URL for gossip envelopes), peers, extensions, core modules (petitions/votes/delegation/groups/federation/topic gardener/social), default group policy, topic gardener, gossip sync controls, media moderation (reports + view requests), outbound delivery summaries, runtime ops metrics (module-disabled + rate-limit counters) plus retention/interval settings, and session overrides without editing JSON. Topic gardener review includes preview panels with confirmation for merge/split, pending rename/merge/split suggestions, a topic history diff view, and a manual sync button. Gossip push/pull controls disable when federation is off or data mode is centralized. - ActivityPub stubs:
/ap/actors/{hash}exposes actor descriptors and/ap/actors/{hash}/outboxserves per-actor posts viasrc/modules/federation/activitypub.js;/ap/outboxserves a global collection;/ap/objects/{id}serves local note objects;/ap/inboxingests preview social notes (ignores direct payloads not addressed to a local actor) and triggers mention/direct notifications. ActivityPub endpoints require federation + social modules to be enabled. - Transactions registry:
/transactionslists local, stamped entries for sensitive actions (petition drafts/status changes, votes, delegation overrides);/transactions/exportemits a signed digest envelope,/transactions/ledgerserves the envelope to peers, and/transactions/gossipingests summaries for cross-provider reconciliation.
- SSR‑first shell lives in
src/public/templates/withlayout.htmlwrapping page templates; status strip surfaces Circle enforcement + validation/preview state so users see why content is gated. Styles/JS insrc/public/app.cssandsrc/public/app.js. - Navigation and panels stay modular: keep links visible only for enabled modules (discussion/forum/social/petitions/delegation/groups) to avoid dead ends; module toggles live in
/adminand extension toggles (/extensionsorCIRCLE_EXTENSIONS) drive policy badges and gate messages. - Preview/provenance cues are consistent across modules (pills for
Previewandfrom {issuer}); whenDATA_PREVIEW=false, the server hides previews, otherwise label them. Social feed shows follow‑type filters + inline replies; discussion/forum reuse the same pill language. - Templating personalization: duplicate templates under
src/public/templates/to theme per deployment (colors/fonts can be tweaked inapp.css); keep SSR placeholders ({{...}}) intact for router partial responses. Avoid SPA‑only components to preserve partial‑render navigation. - Extension UI hooks: use light‑touch badges or info strips to signal tightened policies (extension‑driven gate changes) instead of new flows; policy messages come from
circle/policyso custom extensions display automatically. Notifications and social mentions reuse the same renderer to keep UX coherent across modules.
Defaults live under src/data/:
ledger.json,sessions.json,peers.jsondiscussions.json,petitions.json,signatures.json,votes.json,delegations.json,notifications.jsontopics.json,groups.json,group-policies.json,group-elections.jsonactors.jsonsocial-follows.json,social-posts.json,social-media.jsontransactions.json,transaction-summaries.jsonsettings.json,meta.json
Media binaries live under src/data/media/ and are served through provider‑gated endpoints.
Adapter notes:
adapters/memory.jsis ephemeral.adapters/sql.jsuses SQLite (DATA_SQLITE_URL|FILE, needssqlite3).adapters/kv.jsstores everything in a single KV JSON file.adapters/mysql.jsuses MySQL (mysql2optional dep) with a singlestate_entriestable.adapters/mongodb.jsuses MongoDB (mongodboptional dep) with a singlestate_entriescollection.src/infra/persistence/storage.jsroutes throughsrc/infra/persistence/store.jsto pick the adapter.
- Provider‑local contact fields stay local (no gossip).
- Previews are gated by
DATA_PREVIEW+ validation level. - Redundancy is chosen via
DATA_MODE(centralized/hybrid/p2p) and adapter. - Signatures/validation state decide what can be rendered or replicated.
- Main transactions registry: append‑only log (type, petitionId, actorHash, digest) stored locally in
transactions.json(or adapter equivalent) for sensitive operations (e.g., petition drafts, votes). Entries are stamped with issuer/mode/adapter and validation status;/transactionsexposes recent entries for audit,/healthsurfaces counts/digests plus summary gossip totals. - Transactions summary gossip:
/transactions/exportemits a summary envelope (issuer/mode/adapter, digests) signed whenCIRCLE_PRIVATE_KEYis set;/transactions/ledgerserves the envelope to peers and/transactions/gossipingests summary envelopes intotransaction-summaries.jsonfor reconciliation. Use gossip carefully with strict validation.
- Centralized profile:
DATA_MODE=centralizedwith any adapter; gossip is off (no ingest), all writes go to the chosen store. Best for single‑provider deployments and local dev. Back upsrc/data/(or the DB/KV file) regularly. - Hybrid profile:
DATA_MODE=hybridkeeps a canonical adapter (JSON/SQL/KV/MySQL/MongoDB) while accepting gossip for redundancy/audit. Pair withDATA_VALIDATION_LEVEL=observe|strictto decide if gossip is stored as preview or rejected;DATA_PREVIEW=trueallows staging until validated. Control push/pull frequency withGOSSIP_INTERVAL_SECONDS. - P2P profile:
DATA_MODE=p2ptreats gossip as primary, with an optional local cache. Combine withDATA_PREVIEW=falseandDATA_VALIDATION_LEVEL=strictto only accept signed, validated envelopes. Peers listed in/circle/peersseed synchronization; replication logic lives insrc/modules/federation/replication.js.
- Votes and ledger exports can be signed with
CIRCLE_PRIVATE_KEY(PEM) and verified withCIRCLE_PUBLIC_KEY. - Exports include policy id/version (
CIRCLE_POLICY_ID, defaults in settings). - Admin UI surfaces validation state; UI labels preview vs validated content when
DATA_PREVIEWis enabled.
- Snapshot data before migrations or adapter switches.
- Publish
CIRCLE_PUBLIC_KEYto peers before enabling signatures. - Treat peers as hints, not trust anchors.
- Worker‑based topic gardeners live under
src/infra/workers/and return preview entries gated by validation settings.
src/infra/persistence/migrations.jsnormalizes schema versions (handles/roles, petition lifecycle, delegation/notifications, extensions, group elections, data topology, validationStatus on content/groups/delegations/notifications).- Metadata lives in
meta.json; runningnpm startornpm testapplies upgrades.
npm install
npm test # node:test: hashing, migrations, classification hooks, policy gates, extensions, UI + ring tests
npm run test:ui # Puppeteer UI-only flow
npm start # starts the SSR server
npm run db:check # prints adapter/profile + record counts for the current DATA_ADAPTER- The app is SSR‑first with a vanilla router interceptor: set
X-Requested-With: partialto fetch partial HTML for app‑like navigation. - Static assets/templates live in
src/public; keep new templates undersrc/public/templates.
- No raw PID/PII: persist only blinded hashes. Treat any provider‑local optional fields (email, personal details) as local only and never gossip them.
- Peers are hints, not trust anchors: federation tooling should assume peers can be wrong or malicious; strict validation and quarantine are part of the intended hardening path.
- Audit visibility:
/adminsurfaces ledger hash, gossip ingest state, outbound/inbound gossip sync status, peer health reset actions, recent transactions, outbound delivery summaries, and runtime ops counters;/transactions,/transactions/export,/transactions/ledger, and/transactions/gossipprovide JSON and signed summaries for reconciliation./healthexposes peer health summaries plus vote/transactions gossip added/updated counts, outbound delivery stats, and ops metrics for dashboards, including last peer ledger hash snapshots (match/mismatch). Gossip sync skips peers that disable modules or gossip, so peer health reflects real failures. - Gossip resilience: optional endpoints (votes/transactions) that are missing or disabled are treated as skipped for peer health scoring, so messaging-only peers do not get penalized.
- Backups: if you run with JSON/KV storage, schedule backups of
src/data/(or your DB/KV file), especially before migrations. - Ops metrics persistence: module-disabled + rate-limit counters are snapshotted into
settings.jsonon a rolling window (METRICS_SNAPSHOT_INTERVAL_SECONDS,METRICS_SNAPSHOT_RETENTION_HOURS) so restarts keep recent trends.
- Identity safety: only store blinded PID hashes; never persist wallet-provided PII. Keys used for envelope signing must be rotated carefully; publish
CIRCLE_PUBLIC_KEYto peers before enabling signatures. - Gossip: peers are hints, not trust anchors. Use Circle policy flags +
/healthto monitor enforcement and quarantine toxic providers. - Data durability: JSON stores are append-heavy; schedule backups of
src/data/and watchmeta.jsonwhen migrations bump schema versions. - Topic gardener: Python/ML helpers are expected to live under
src/infra/workers/(seeprinciple-docs/DynamicTopicCategorization.md); Node callers should reconcile provider outputs to avoid conflicting labels.
ROADMAP.md— phased delivery plan and sequencing rationaleAGENTS.md— implementation directives and vocabulary contractprinciple-docs/RepresentativeParties.md— design principles and thesisprinciple-docs/DynamicTopicCategorization.md— topic gardener spec
If you want to contribute, start by reading AGENTS.md and keep changes aligned with the vocabulary and privacy constraints. Small, testable modules are preferred over deep rewrites.