Repository: https://codeberg.org/eorg/web-org
A communication-first modular organization kernel that can begin as shared messaging and grow into protected voice, responsibility, hierarchy, authority, resource, sector, pressure-learning, civic-governance, and federation workflows.
This repository is a Node.js SSR application that starts as a usable email/discussion + notification workspace, then lets an organization progressively enable higher‑stakes modules without adopting one fixed operating model. The former party-centric product name is retired; Party Circle remains an optional civic policy bundle. Its identity layer can enforce one natural person = one voice without storing raw PII when that bundle is enabled.
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.
The framework has two halves, and each has a walkthrough driven end-to-end through the interface itself — one browser session per member, an assertion at every step, so a broken flow fails the run instead of quietly producing a nice picture.
- A party decides its own position exercises the civic half: verified people, deliberation before voting, liquid delegation, and soft-power accountability. It follows
principle-docs/RepresentativeParties.mdand Scenario A ofprinciple-docs/SimulationScenarios.md. - An organization takes a concern to a decision exercises the operations half: a message becoming a case, pressure the organization can see, and structure earned by use. It follows
principle-docs/OrganizationPressureManagement.md.
Members of Rete Civica Nazionale — a fictional Italian civic party — take a real Italian policy question from a message to a decision, under strict Circle enforcement (one verified person = one voice). The in-app content is in Italian and the norms are the real ones (L. 120/2007 and L. 107/2024 on intramoenia and CUP monitoring, the PNGLA priority classes, sent. 192/2024 on differentiated autonomy); the people and the institution are invented.
Every screenshot below is produced by scripts/party-journeys.js, which drives these flows through the interface — one browser session per member — and asserts the outcome at each step.
ENFORCE_CIRCLE=true CIRCLE_POLICY_ID=rete-civica-nazionale \
DATA_VALIDATION_LEVEL=strict DATA_PREVIEW=false ./run-standalone.sh # terminal 1
BASE_URL=http://127.0.0.1:3000 OUT_DIR=md-assets/party \
node scripts/party-journeys.js # terminal 2Nothing here compels a representative: a member could vote against the platform's own outcome and the record would simply show it. The free mandate (art. 67 Cost.) stays intact; the accountability is that no one can quietly rewrite what happened.
Six members of a care cooperative — @nadia (night nursing), @tomas (pharmacy), @ines (delegate), @koen (logistics), @mara (works council), and the provider admin — take one concern from a message to a decision.
Every screenshot below is produced by scripts/user-journeys.js, which drives these flows through the interface itself: clicking the controls, typing into the fields, one browser session per member. It asserts the outcome at each step, so a broken flow fails the run instead of quietly producing a nice picture.
./run-standalone.sh # terminal 1
BASE_URL=http://127.0.0.1:3000 node scripts/user-journeys.js # terminal 2Delegation stays topic-scoped, advisory, and revocable: cycles, banned delegates, and overlong chains are stopped automatically.
Many organization and civic platforms fail in one of three 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.
- They impose a complete organization model before earning adoption. People are asked to replace familiar communication with forms, taxonomies, and dashboards all at once.
This project takes the opposite stance:
- Start with familiar communication and accountability cues.
- Promote a message into a structured case only when ownership, protection, authority, resources, mediation, or learning adds value.
- 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).
- Care providers, companies, cooperatives, associations, unions, NGOs, civic committees, 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, with evidence/citation links supported on discussion notes and forum comments.
- Organization pressure workspace:
/organizationdemonstrates email-first intake, needs/concerns/advice, protected visibility, responsibilities, formal-vs-actual structure, explicit powers, funds, sectors, mediation, aggregate pressure dynamics, and impact learning as independent layers. - 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.
- Provider-blind option (scaffold):
/adminpolicy fields for observe/strict, ring registry gossip, and ringSignature placeholders for votes/transactions; issuer escrow + mixnet transport planned. - Privilege-free genesis (optional): Circles can bootstrap without preassigned roles beyond provider admins; groups and member votes can evolve governance structures over time.
- Petitions → votes pipeline: proposals with evidence summaries + source links, structured outline fields (problem/action/impact/risks/plan), collaborative revisions + version history, revision diffs + pre‑vote freeze, signatures/quorum, deliberation feed with stance tags (support/concern/question) + fact‑check flags + evidence/citation prompts, and argument‑map linking for pro/con trees.
- Governance defaults: admin-managed constitution defaults (quorum, discussion/vote windows, vote method) with verified-user change proposals, admin decisions, and public decision history.
- Module control center: admin toggles plus a settings map with jump links so module activation and configuration stay in one place (links are defined alongside module metadata).
- Module lanes: optional modules are grouped by lane (
Organization operations,P2P social/community,Civic governance,Federation/resilience) so deployments can compose only the operating model they need. - Operational home workspace:
/stays useful for guests and signed-in members, showing access gates, recent lane activity, and operator cues instead of acting like a brochure or a forced redirect. - 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. A delegation on a category also covers its subcategories, narrowest match first — and proposals carry the category their author chose, so those delegations actually reach them.
- Delegated ballots with a deadline: handing your vote to your delegate records a real ballot, not an abstention. It resolves through the delegate's own vote (transitively), stays uncounted while it waits, lapses to abstain if the delegate misses the window (
delegateVoteWindowDays, default 5), and you can override it yourself at any time. - Branch groups: a group can be a subgroup of another — a party with a think tank, a working wing, or an alternative line. Nesting is structural, not authority: a branch keeps its own membership and cachets.
- Group categories + directory: groups declare the categories they discuss, and
/groupsanswers "who works on what" — each category, the groups covering it, and who they put forward — with one-click delegation. - Optional group lanes: a group can switch on its own internal discussion and proposal lanes (off by default, members-only or public to read). Internal items stay out of the Circle feed until promoted.
- Promote to the upper level: a group discussion or proposal can be moved to the parent group, or to the whole Circle. It is a move, not a copy — comments, signatures and votes travel with it. Ordinary members can request a promotion; group admins and lane moderators decide, and a decline carries a reason.
- Group roles:
member,lane moderator(can act on lane items but not on what defines the group), andadmin. - Branch election roll-up (optional): a parent group can opt in to adopting a branch's closed election for a category it has not voted on itself, always labelled with the branch it came from.
- Delegate profiles: view delegation counts, vote history, and group endorsements (optional
publicBioprofile field). - Federation scaffolding + redundancy knobs: gossip endpoints, signed envelopes, and ActivityPub inbox ingestion (preview‑gated) for auditability.
- Libp2p readiness (optional): dependency included to evaluate P2P transport and future Circle key continuity not tied to the first provider.
- Peer ledger audits: peer health tracks last ledger hash snapshots (match/mismatch) in
/adminand/health. - Peer capability hints: ledger gossip includes module/data-mode snapshots so
/admincan show peer capabilities and gossip skips disabled endpoints. - Gossip efficiency: peer capability hints are fingerprinted and provider-blind announcements are cached by
updatedAtto reduce repeated gossip normalization work. - Gossip pull skip-cache: optional endpoint probes (
/votes/ledger,/transactions/ledger) are cached after module-disabled/optional misses so repeated pulls avoid redundant failures; cache status is visible in/adminand/health. - Gossip push skip-cache: outbound endpoint posts (
/circle/gossip,/votes/gossip,/transactions/gossip) are cached after module-disabled/gossip-disabled responses so repeated pushes avoid redundant retries; cache status is visible in/adminand/health. - Sticky composer UX: social composer auto-collapses on downward scroll to keep the wall readable while preserving manual expand controls.
- Practical interface controls: top bar includes a persistent Interface panel — appearance (accent hue + density), per-page section show/hide, large text, reduced motion, compact nav, composer auto-collapse preference, and partial-navigation toggle — with inline save-to-session-defaults, plus a side-nav jump filter with
Ctrl/Cmd+Kand/shortcuts. The theme is built on an HSL semantic-token system so one accent knob re-tints the whole UI. - 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.
- Extension lifecycle: discover provider-local extensions, record installed versions, install/update/uninstall safely, and enable runtime hooks without forking (
src/modules/extensions/,CIRCLE_EXTENSIONS).
- Handles, roles, moderation flags, notifications
- SSR pages + partial‑HTML navigation (fast, indexable, minimal JS)
- Protected voice modes for needs, constructive concerns, advice, resource consultations, and disputes
- Responsibilities, contextual hierarchies, explicit authority, financial context, sectors, mediation, pressure dynamics, and impact review
- 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). Signing keys are exported as PEM text — passing a file path leaves envelopes unsigned and /admin reports the key as ignored rather than loaded.
# Drive the organization-operations flows through the UI (discussion, proposal,
# vote, groups, delegation) and capture that walkthrough's screenshots
BASE_URL=http://127.0.0.1:3000 node scripts/user-journeys.js
# Drive the representative-party flows (Scenario A: Rete Civica Nazionale) and
# capture the party walkthrough. Needs a strict-Circle standalone:
# ENFORCE_CIRCLE=true CIRCLE_POLICY_ID=rete-civica-nazionale \
# DATA_VALIDATION_LEVEL=strict DATA_PREVIEW=false ./run-standalone.sh
BASE_URL=http://127.0.0.1:3000 node scripts/party-journeys.js
# Fill a running deployment with a plausible demo organization
BASE_URL=http://127.0.0.1:3000 node scripts/seed-demo.js
# Capture one screenshot per route + a layout audit (clipping, blocked clicks)
BASE_URL=http://127.0.0.1:3000 node scripts/ui-screenshots.jsuser-journeys.js gives each member their own browser context, so quorum and
"another member sees it" are checked against genuinely distinct sessions. Use
HEADLESS=false to watch a run, or ONLY=proposal,vote to drive one track.
On first run, visit /admin to complete the installation wizard (Circle basics, data mode, peer alignment, optional defaults). Saving persists settings to the configured adapter (JSON/KV under src/data/ by default). The module control center in /admin keeps toggles and related settings linked with jump anchors.
Claim an admin session before exposing the provider. A deployment with no admin session is unowned:
/adminhas to stay reachable for the wizard to run at all, and the page says so in a warning. Assign theadminrole to a verified session as the last setup step (/admin→ Session overrides). Access latches to admin sessions the moment one exists, and never reopens. Until then, treat the deployment as reachable only from a trusted network.
# 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="$(cat ./priv.pem)" CIRCLE_PUBLIC_KEY="$(cat ./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. The test runner will skip these checks with a reason if the servers are unreachable. - The UI suite also runs a MySQL-backed restart persistence flow when
TEST_MYSQL_*variables are configured (supports optionalTEST_MYSQL_TABLE_PREFIXfor isolated table names) and verifies persisted discussion/UI defaults plus module-toggle disabled-endpoint UX after restart.
- Server:
http://0.0.0.0:3000 - JSON persistence (default): writes under
src/data/
- Social media uploads stream to
src/data/uploadsbefore moving intosrc/data/media; stale temp files are pruned on startup. - Cleanup stats (last run + cleaned counts) are visible in
/healthand/adminand persist across restarts via settings. - Knobs:
SOCIAL_MEDIA_MAX_BYTES,SOCIAL_MEDIA_FIELD_MAX_BYTES,SOCIAL_MEDIA_FIELD_TOTAL_MAX_BYTES,SOCIAL_MEDIA_FIELD_MAX_COUNT,SOCIAL_MEDIA_FIELD_NAME_MAX_BYTES,SOCIAL_MEDIA_HEADER_MAX_BYTES,SOCIAL_MEDIA_FILENAME_MAX_BYTES,SOCIAL_MEDIA_UPLOADS_ROOT,SOCIAL_MEDIA_UPLOAD_RETENTION_HOURS,SOCIAL_MEDIA_UPLOAD_CLEANUP_MINUTES.
- Non-media endpoints enforce
REQUEST_BODY_MAX_BYTES(default 1MB) and reject multipart form data by default. - Set
REQUEST_BODY_REJECT_MULTIPART=falseif you need multipart payloads outside the social media uploads path.
GOSSIP_PEER_CAPABILITIES_MAX_AGE_HOURScontrols how long cached peer capability hints are trusted before re-probing (default 168h).GOSSIP_OPTIONAL_SKIP_SECONDScontrols how long gossip skip-cache entries live (optional pull endpoints and outbound push skips after module-disabled/not-found responses; default 3600s).
- Interface preferences are stored in browser localStorage (
organization-web-ui-settings) and applied across partial/full navigations. The app reads the formerparty-web-ui-settingskey once for a lossless upgrade. - The top-bar Interface panel can persist the current settings into session defaults without leaving the active page (
Save as session defaults), andUse session defaultsclears local overrides. Saved settings also render on the initial server response (on the<html>element), so a member's appearance holds even with JavaScript disabled. - Appearance (per member, non-binding on anyone else): an accent-hue picker (six presets driving a single
--primary-hthat re-tints the whole HSL token system), a theme control (dark / light / system), and a density control (compact / cozy / relaxed). The visual system is modern-dark by default. Choosing system follows the operating system's light/dark preference; it is resolved by a CSSprefers-color-schemequery rather than JavaScript, so it applies on the first paint with no flash and works with JavaScript disabled. Both themes are audited to WCAG AA contrast on every member-facing route. - Sections: a per-page show/hide list of the module panels on the current page. Unchecking a section hides it from the member's own view only — applied server-side (the panel is stamped
data-member-hidden) and live in the browser. This is member-scoped "ad hoc modularity"; the admin-only Composer remains the tool for deployment-wide brand/layout defaults. - Other toggles: large text, reduced motion, compact nav, keep social composer open while scrolling, and fast partial navigation (off switches links/forms back to full-page loads).
- Side navigation includes a jump filter with keyboard shortcuts:
Ctrl/Cmd+Kfocuses search,/focuses search when not typing,Escclears.
/landing,/healthmetrics/organizationcommunication-first organization pressure template and organizational mirror/auth/eudistart verification,/auth/callbackreturn/discussion,/forum,/notifications,/delegation/profile(provider‑local profile attributes editor)/governance(public constitution defaults + community change proposals),/governance/proposals(POST verified-user change proposal)/delegates/{hash}(delegate profile summary)/petitionsand/petitions/*(when enabled),/votes/*(when enabled)/social/*micro‑posts, follows, and media uploads (/social/media/{id},/social/media/report)/adminsettings, first-run installation wizard, policy toggles, rate-limit overrides, and audit summaries;/extensionsextension catalog, lifecycle, and runtime toggles/transactions,/transactions/export,/transactions/ledger,/transactions/gossipfor local audit + cross‑provider reconciliation/circle/*gossip/ledger/peers (federation scaffolding + peer capability hints),/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.
- Organization-pressure adoption: email stays useful by itself; protected voice, responsibility, hierarchy, authority, funds, sectors, mediation, aggregate pressure, and impact attach to the same source record only when enabled. See
principle-docs/OrganizationPressureManagement.md. - 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.
Optional modules are organized in four lanes so you can deploy only the layer you need: Organization operations, P2P social/community, Civic governance, and Federation/resilience.
- Coordination workspace: familiar message intake, bounded replies, manual case promotion, and searchable history.
- Protected voice: permission-aware attributed, role-visible, department-visible, and protected intake modes.
- Responsibilities and hierarchy: outcome owners, action commitments, contextual escalation, and formal-vs-actual working flow.
- Authority and funds: explicit decision rights plus visible resource constraints, accountable decisions, execution, and reconciliation.
- Sectors: multiple views over one canonical case rather than duplicated queues.
- Mediation, pressure dynamics, and impact: protected pacification, aggregate system signals, recurrence, and observed learning.
- Discussion + forum: threaded posts/comments with SSR UI, plus optional evidence links on discussion notes and forum comments.
- 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, with locked media view requests notifying authors.
- Petitions: draft proposals with structured outlines, collaborative revisions with history + diffs, signatures/quorum, stage transitions, pre‑vote freeze, stance-tagged discussion feed with evidence/citation prompts, and argument‑map linking.
- 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. - Governance defaults: admin-set constitution settings surfaced under
/governance, with verified-user change proposals, admin approve/reject decisions, and auditable history. - 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.
- Provider-blind scaffolding: ring registry announcements and optional ringSignature envelopes for votes/transactions (observe/strict policy modes).
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 (/circle/ledger, /votes/ledger, /transactions/ledger) respect preview gating and omit preview entries when DATA_PREVIEW=false; they gzip-compress responses when Accept-Encoding: gzip is provided and the payload is at least ~2KB.
- Server:
HOST,PORT - Circle/policy:
ENFORCE_CIRCLE,CIRCLE_POLICY_ID,CIRCLE_ISSUER,GOSSIP_INTERVAL_SECONDS,GOSSIP_OPTIONAL_SKIP_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/; initial migrations register these as installed and enabled) - Signing:
CIRCLE_PRIVATE_KEY,CIRCLE_PUBLIC_KEY(PEM). Usenpm run keys:circlefor PEM + .env‑ready output (optional libp2p ring ID included). SetCIRCLE_RING_IDto prefill the provider-blind ring ID in/admin./admincan generate and persist keys intosettings.json; env vars still override on restart. - 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) - Request body limits:
REQUEST_BODY_MAX_BYTES(default 1MB),REQUEST_BODY_REJECT_MULTIPART(default true) - 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),SOCIAL_MEDIA_FILENAME_MAX_BYTES(default 256)
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="$(cat /path/pub.pem)" CIRCLE_PRIVATE_KEY="$(cat /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;/adminand/extensionsexpose discovered version metadata, non-destructive install/update/uninstall registration, and separate runtime enable/disable controls. - 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="$(cat /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/modules/<module>/http/<module>.jsfor UI modules, plus non-UI endpoints insrc/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/modules/<module>/templates/(core layout undersrc/modules/core/templates/layout.html, partials undertemplates/partials/) - Styles/JS:
src/public/app.css,src/public/app.js
- Registry:
src/modules/extensions/registry.js - Sample:
sample-policy-tighten.js(versioned as1.0.0; bootstrap viaCIRCLE_EXTENSIONSor install and enable from/admin) - Lifecycle state:
settings.extensionInstallationsrecords provider-local source, installed version, and install/update timestamps; uninstall disables hooks and deregisters the extension without deleting its source file.
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/modules/auth/http/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 + capability hints,/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 (view requests notify authors), 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 + evidence/citation prompts), 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, matching a topic against its full path so a parent-category delegation covers its subcategories;src/modules/delegation/delegatedBallots.jsresolves delegated ballots through the delegate's own vote and enforces the delegate vote window./delegationmanages manual preferences, lists ballots delegated to you (with deadline countdowns) and ballots you handed to a delegate (with an override link), 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, nests groups into branches, declares group categories for the/groupscategory directory, gates optional internal discussion/proposal lanes, and moves items up a level via/petitions/promoteand/discussion(intent=promote); 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, and optional modules grouped by lane (Organization operations,P2P social/community,Civic governance,Federation/resilience), plus default group policy, topic gardener, gossip sync controls, media moderation (reports + view requests), persisted upload cleanup status, 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. Signing key status + keygen hints surface here; admin can generate signing keys intosettings.json(env overrides on restart); provider-blind ring keys accept PEM blocks separated by blank lines (or one-line PEM with\nescapes). - 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/modules/<module>/templates/withsrc/modules/core/templates/layout.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. - Verbosity controls: admin sets the default UI verbosity (full vs compact), verified users can override per session under
/profileor from the top‑bar toggle, and admins can set per‑page presets via the Composer tool. - Composer mode: admin-only floating tool for inline text/style edits, DOM order tweaks for composer-keyed elements, nav ordering, per-page layout (drag panels between columns, hide panels), and per-page verbosity presets.
- Composer overrides persist per template in
settings.composer.pages[pageKey](text/style/dom order), while layout + nav overrides remain global insettings.composer. - 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/modules/<module>/templates/(andtemplates/partials/) 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. - Template partials:
{{> name}}resolvestemplates/partials/name.htmlwithin the module; use{{> module:name}}for cross-module includes when needed. - 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; when disabled, preview payloads are rejected even inobserve/offvalidation modes. Validation level only decides whether allowed previews stay marked as preview or arrive as validated. - 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 (including cached capability hints); 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, versioned extension installations, 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 live in
src/public; keep new templates undersrc/modules/<module>/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 + durations, peer health reset actions, peer capability hints, pull/push skip-cache summaries, recent transactions, outbound delivery summaries, persisted upload cleanup stats, 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, gossip durations, pull/push skip-cache counts, outbound delivery stats, persisted upload cleanup 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, and outbound skip-cache avoids repeated retries to peers that currently disable gossip endpoints.
- 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/OrganizationPressureManagement.md— organization-pressure modelprinciple-docs/SimulationScenarios.md— the two reference scenarios (a fictional Italian party; a fictional ASL) used to seed deployments, write tests, and take screenshotsprinciple-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.



























