Crumpet Media β Technical Architecture (v0 + v0.1)
0) Glossary (ELI5)
Article package (AOM): a shoebox with an index card, the story, and pictures. Its fingerprint (CID) proves nothing inside has changed.
CID: IPFS fingerprint; if the content changes, the fingerprint changes.
Score: Upvotes (counted CRUMBS) minus Downvotes (25/50 CRUMBS).
Vault: protocol treasury (downvotes, some upvote share, slashed bonds, unrefunded flags).
Flag: a βplease reviewβ deposit. Refunded if DAO takes action; else kept by Vault.
CIN (Layerβ3 node): a server that fetches articles, builds language search indexes, and serves fast queries. Nodes crossβcheck each other so you donβt trust just one.
1) Article Object Model (AOM v1)
Package layout (UnixFS folder or CAR):
bashCopyEdit/crumpet.json # DAG-CBOR manifest (also emitted as JSON for devs)
/body.md # Markdown (safe subset)
/preview.html # OPTIONAL deterministic HTML, trust-but-verify
/media/<hash>.<ext> # images; referenced by relative paths in body.mdConstraints (v0):
Max package size: 4β―MB
β€ 10 images, max 2560px long edge
No embeds (no iframes/scripts). Images only, bundled locally.
Manifest (JSON view):
Determinism: A reference Rust renderer (WASM in browser). If /preview.html mismatches a local render of /body.md, the client discards it and uses the local render.
2) TokenβEconomics & User Actions (SingleβAction Rule)
One action per wallet per article. A wallet may perform exactly one among:
Upvote (with optional extra tip),
Dislike (β25),
DownVote (β50),
Flag (deposit for review).
Once recorded, the wallet cannot perform any other action on that article (no second vote, no flag after voting, etc.). This prevents gaming and ensures fairness.
ELI5: You get one button press per article. Choose wisely.
3) Voting & Scoring (Linear, capped; unlimited tips)
DAOβtunable parameters (timelocked):
MIN_UP = 10 CRUMBS(minimum to cast an upvote)MAX_UP_WEIGHT = 100 CRUMBS(perβvote counted cap; overflow becomes tip)DISLIKE = 25 CRUMBS(counts β25; 100% to Vault)DOWNVOTE = 50 CRUMBS(counts β50; 100% to Vault)
Upvote handling (example):
Send
amount. Ifamount < MIN_UP, revert.counted = min(amount, MAX_UP_WEIGHT)β contributes +counted to score.tip = amount - countedβ extra support, no extra weight.Split: of the counted part, 20% to Vault, 80% to Author; 100% of tips to Author.
Down actions:
Dislike deducts 25 from score; 25 CRUMBS β Vault.
DownVote deducts 50; 50 CRUMBS β Vault.
Score: score = βcountedUp β βdownAmounts.
Why walletβsplit is neutral: cost per counted unit is 1 CRUMBS; the cap is per vote, and you only get one vote per article. Splitting across wallets is Sybil behavior the singleβaction rule removes at the origin.
4) Flags, Disputes, and Bonds (by time, not blocks)
DAOβtunable:
FLAG_FEE = 25 CRUMBS(refunded if action taken)FLAGS_TO_OPEN = 3GRACE_SECONDS = 10 days(timeβbased, not blockβbased)PUBLISH_BOND = 100 CRUMBS(posted by author on publish)
Lifecycle:
Publish: author posts B to the contract.
Flag: any wallet can flag (once per article) by paying F; optional
noteCidfor context.A flag consumes the oneβaction allowance for that wallet on that article.
Dispute: when a CID has K flags, a case opens.
Resolve (DAO):
Action taken = true: refund F to all flaggers in that case; slash B to Vault (if still before
publish.createdAt + GRACE_SECONDS); attach Community Notes (IPFS CIDs).No action: flags kept by Vault; authorβs bond autoβrefundable after
GRACE_SECONDS.
Refunds:
autoRefundBond(cid)callable by anyone oncenow β₯ publish.createdAt + GRACE_SECONDSand thereβs no upheld action.Flag refunds (on action taken) are pullβbased: each flagger calls
claimFlagRefund(caseId).
ELI5: You leave a small deposit when you publish. If a valid complaint is upheld, your deposit is taken. If not, you get it back after ~10 days. Flaggers get their deposit back only if the DAO acts.
5) Contracts (Kasplex EVM) β Key Interfaces
Modular, with a separate Config contract (DAOβtimelocked). Events are explicit for indexers and analytics.
5.1 ArticlesRegistry
5.2 Actions (Upvote/Down/Flag) with SingleβAction Enforcement
5.3 Disputes & Bonds (timeβbased grace)
5.4 Vault & Node Rewards
Vault accrues: 20% counted upvotes, 100% downvotes, unrefunded flags, slashed bonds.
DAO allocates up to 40% of Vault net inflows to Node Rewards Pool.
5.5 Config (DAOβtimelocked)
Parameters:
MIN_UP,MAX_UP_WEIGHT,DISLIKE,DOWNVOTE,FLAG_FEE,FLAGS_TO_OPEN,GRACE_SECONDS,PUBLISH_BOND, NodeRewards splits/percentages.Emits
ConfigChanged(...).
6) Layerβ3: CIN Nodes (Storage + Indexing + Serving)
6.1 Protocols & Transport (lowβlatency, modern)
NodeβNode (control/data):
gRPC over HTTP/2 (Rust
tonic) for control plane (snapshots, health, attestations).libp2p (Noise) over QUIC for P2P gossip (language snapshot announcements) and fast peer discovery.
HTTP/3 (QUIC) and IPFS for snapshot/package transfers (concurrent range fetches).
NodeβUI (client apps):
HTTP/2 JSON API (search/article/trending).
WebSocket streams for live updates (
/ws/events), low latency.Optional gRPCβWeb (via Envoy) if you prefer a single RPC stack in the browser.
ELI5: We use the fast lanes on the internet (HTTP/2, QUIC, WebSockets, gRPC) so searches feel instant and nodes share data quickly.
6.2 Federation & Integrity (languageβsharded)
Each node builds perβlanguage index segments for its primary languages (
en,de, β¦).Nodes fetch signed snapshots for other languages from peers, requiring 2βofβ3 matching Merkle roots.
Regular CID spot checks verify fetched segments arenβt lying.
Snapshots record renderer/analyzer versions; mismatches are rejected.
6.3 Incentives (from Vault)
Node Rewards Pool gets up to 40% of Vault net inflows (DAOβtunable).
Epoch payouts (e.g., weekly):
40% Uptime share β pass SLA probes.
40% Build share β # of language snapshots built that achieved majority/committee acceptance.
20% Serve share β optional, via hashed βservedβ receipts (rateβlimited to prevent gaming).
6.4 Hardware targets
Builder (2 languages): 2 vCPU, 4β8β―GB RAM, 100β―GB SSD, local IPFS node.
Fetcher/serveβonly: 2 vCPU, 4β―GB RAM, 50β―GB SSD.
Pruning: retain last N snapshots; older on IPFS.
6.5 Node APIs
HTTP/2 JSON:
gRPC (excerpt, proto sketch):
7) Frontend & UX
Next.js + TypeScript, wagmi/RainbowKit on Kasplex EVM.
Renderer: RustβWASM; Sanitizer: strict allowβlist.
Service Worker: cache AOM packages; multiβsource fetch race (local IPFS β node cache β gateways).
Publish UX: require 3 IPFS pinβacks before enabling onβchain publish.
Vote UX: show one chooser (Upvote/Dislike/DownVote/Flag). After selection, lock UI.
Dispute UX: show label and Community Notes if action taken; do not remove content.
8) Internationalization
langrequired in manifest.Dayβ1 analyzers: EN, DE, ES, FR, PT, IT, NL, PL, TR, RU, ZHβHans, ZHβHant, JA, KO, AR, HI.
Browsersβ builtβin translation works (plain HTML).
Adding languages = add analyzer + tests (no AOM change).
9) Security & Best Practices
Contracts: Reentrancy guards, ChecksβEffectsβInteractions, SafeERC20, pull refunds (flags).
Governance: Timelock on Config; multisig; parameter change rate limits.
Time sources: use
block.timestampas wallβclock forGRACE_SECONDS. Avoid assuming fixed block cadence.Indexers: reject snapshots with renderer/analyzer major version mismatches; keep a denylist of malicious peers.
Content: validate image types/sizes; strip scripts/event handlers; forbid externalβscheme links.
10) Examples & Edge Cases
Singleβaction constraint:
If a wallet Upvoted, it cannot later Dislike, DownVote, or Flag.
If a wallet Flagged, it cannot later Upvote or DownVote.
Attempts revert with
AlreadyActed().
Upvote overflow:
Send 120 CRUMBS β counted 100, tip 20; one vote recorded; canβt vote again.
Flags & refunds:
Case opened at 3 flags.
DAO action taken: flaggers each call
claimFlagRefund(25 back).No action within 10 days: authorβs
autoRefundBondsucceeds; flags stay in Vault.
Article updates:
New edition = new CID;
previousCidlinks history. Scores are perβedition; UI may aggregate.
11) Parameter Table (initial defaults)
MIN_UP
10 CRUMBS
per vote minimum
MAX_UP_WEIGHT
100 CRUMBS
per vote cap for counted weight
DISLIKE
25 CRUMBS
β25; 100% to Vault
DOWNVOTE
50 CRUMBS
β50; 100% to Vault
Upvote split
20% of counted β Vault
80% counted + 100% tips β Author
FLAG_FEE
25 CRUMBS
refunded iff action taken
FLAGS_TO_OPEN
3
threshold to open case
GRACE_SECONDS
10 days
timeβbased
PUBLISH_BOND
100 CRUMBS
slashed on action within grace
Node Rewards Pool
up to 40% Vault net inflow
DAOβtunable
Node split
40% uptime / 40% build / 20% serve
DAOβtunable
12) Repo Skeleton
Example proto (snippet)
proto (snippet)13) CONTRIBUTING.md (short)
Coding standards
Contracts: Solidity ^0.8.24, NatSpec, events for all state changes, Foundry tests (unit/fuzz/invariant), Slither/Static analysis in CI.
Rust: stable toolchain,
clippyclean,rustfmt, property tests where applicable, no unsafe unless justified.TypeScript: strict TS, ESLint+Prettier, never ignore types for chain amounts; use BigInt/ethers.js
BigNumber.
Testing
Contracts: 100% branch coverage targets for Actions/Disputes/Vault; reentrancy + refund invariants.
Renderer: golden vectors, sanitizer adversarial cases (XSS payload corpus).
Indexer: determinism checks (same inputs β identical snapshot roots), multilingual tokenizer tests, p95 latency benchmarks.
Security
No external calls before state changes (CEI).
Use
pullpatterns for refunds (flags).Limit parameter change rates in Config.
Document threat models (Sybil, brigading, snapshot forgery).
CI
Lint, build, test (contracts, Rust, web).
Snapshot reproducibility check on deterministic inputs.
Container image builds & SBOM.
14) Engineer Handβoff Checklist
Final notes
Singleβaction rule is enforced onβchain and reflected in UI (buttons lock after an action).
Grace periods are timeβbased (
GRACE_SECONDS) so they work with a based rollup.Connectivity uses gRPC/HTTP2/WebSockets/QUIC for speed; libp2p gossip advertises snapshots; IPFS (HTTP/3 where available) moves the bytes.
Everything is DAOβtunable with a timelock; indexers include the active parameter set in snapshot metadata.
Last updated