A browser app to create, browse, respond to, and tally on-chain surveys and polls on Cardano, implementing CIP-179 (transaction metadata label 17).
Surveys live entirely in transaction metadata — no smart contracts, no custom backend required to read them. Responses can be public or sealed (timelock-encrypted with drand for delayed reveal), eligibility is scoped by on-chain role (DRep, SPO, CC, Stakeholder, Keyholder), and tallies are computed client-side directly from chain data.
Status: active development. The frontend (explore, results, wallet, respond, create, cancel, sealed mode, IPFS enrichment, governance linkage) is functional, as is the Tier-1 serving backend (Koios read path cached server-side; runs as a Node process or a Cloudflare Worker). The backend also validates responses (deadline, credential proof, dedup), snapshots stake/voting-power weights at each survey's end epoch, and finalizes closed surveys into content-addressed, re-verifiable result artifacts that the app renders as final weighted results;
packages/verifierre-derives any artifact from chain data and checks the hash. Seebackend/ARCHITECTURE.md.
A survey can be advertised by a Conway governance Info Action (CIP-179 Action → Survey linkage): the action's anchor metadata carries the survey's ref, and Tessera surfaces the link on the explore and survey pages once the action's voting deadline matches the survey's end epoch.
The app ships a small helper page at /propose-info-action to build, sign,
and submit that Info Action from a CIP-30 wallet: load a CIP-108 anchor
document, and the page validates its shape, extracts the linked survey, and
checks the epoch alignment before letting you submit. You can pin the exact
anchor bytes to your configured IPFS providers (or host them yourself) so the
served document matches the on-chain hash.
| Path | What it is |
|---|---|
frontend/app |
The browser app — SolidJS + Vite + TypeScript. |
packages/cip179 |
The reusable cip-179 package: label-17 codec plus the cross-implementation domain / tally / txproof / tlock surface. |
packages/core |
Tessera app core (cardano-tessera-core): the DataSource seam, Explore list/health payloads, keyset paging, the survey-list aggregation adapter, and config. The reusable domain/tally surface lives in cip-179. |
packages/koios |
The Koios read path (KoiosDataSource, tally inputs), shared by direct mode, backend, and verifier. |
packages/respond-core |
cardano-tessera-respond-core: the pure, framework-free answering core (drafting, responder eligibility, i18n factory, lazy sealed wrapper) shared by the app and the widget. |
packages/respond-widget |
cardano-tessera-respond: the embeddable <tessera-respond> custom element — answer a survey anywhere, emitting a ready-to-attach label-17 payload. Framework-agnostic; wallets/chain stay host-side. |
packages/respond-react |
cardano-tessera-respond-react: React 18/19 bindings for the widget — typed props synced as DOM properties, tessera:* events as callbacks. |
packages/verifier |
Standalone CLI that re-derives a survey's result artifact from chain data and checks its content hash. |
examples/ |
Minimal React and Svelte host apps for the widget, built in CI so framework compatibility regressions fail a build. |
backend/server |
Tier-1 serving backend: cached chain reads, response validation, weight snapshots, artifact finalization. Node or CF+D1. |
backend/deps |
Indexer submodules (Adder / Yaci Store / Oura) for a future Tier-2; design notes in backend/*.md. |
Requires Node ≥ 22.5 and pnpm ≥ 10 (install pnpm). This is a pnpm workspace — install once at the repository root:
pnpm install
pnpm --filter cardano-tessera-backend dev # terminal 1
VITE_INDEXER_URL=http://localhost:8787 pnpm --filter tessera-app dev # terminal 2The app serves at http://127.0.0.1:3000, reading chain data through the local backend — no Koios token needed, for reads or for building transactions (they are signed and submitted by your CIP-30 wallet).
To avoid retyping the prefix every run, put it in a git-ignored
frontend/app/.env.local instead — Vite loads it in every mode, so a plain
pnpm --filter tessera-app dev picks it up:
# frontend/app/.env.local
VITE_INDEXER_URL=http://localhost:8787Without it (a bare pnpm dev and no .env override), the app falls back to
Direct mode and browser reads to Koios are CORS-blocked — see below.
Alternatively, skip the backend and let the browser scan Koios
directly (the power-user/offline path) by leaving VITE_INDEXER_URL unset.
That path requires an authenticated Koios token (tier 1 is free): the anonymous
tier does not send CORS headers, so browser requests need one. Paste it in the
app's Settings, or set VITE_KOIOS_TOKEN in frontend/app/.env.
Copy frontend/app/.env.example to frontend/app/.env; every variable is
optional and documented there. The main ones:
VITE_NETWORK—preview(default) ormainnet. One deployment serves one network (no runtime switch); the header links to the counterpart app whenVITE_OTHER_NETWORK_URLis set.VITE_INDEXER_URL— the Tier-1 backend for that network. The app verifies the backend serves the same network (via its/health) and refuses a mismatch. Overridable per network in Settings.
IPFS reads race a built-in list of public gateways (no config); IPFS pinning (for authoring external content / rationales) uses per-provider API tokens entered in Settings, stored only in the browser.
The repo is a pnpm workspace (frontend/app, packages/cip179,
packages/core, packages/koios, packages/respond-core,
packages/respond-react, packages/respond-widget, packages/verifier,
backend/server, plus the examples/ host apps).
Packages are consumed from TypeScript source (Vite aliases / exports pointing
at src), so cross-package edits are live with no build step.
From the repository root:
| Command | What it does |
|---|---|
pnpm -r type-check |
Type-check every package. |
pnpm -r test |
Run every package's unit tests (Vitest). |
pnpm --filter tessera-app dev |
Start the app's Vite dev server. |
pnpm --filter cardano-tessera-backend dev |
Run the Tier-1 backend locally (see its README). |
pnpm --filter tessera-app build |
Production build of the app. |
pnpm --filter cardano-tessera-verifier verify -- --backend <url> --survey <tx>:<i> |
Re-verify a survey's final result artifact from chain data. |
Formatting is Prettier (pnpm format / pnpm format:check in frontend/app).
Both halves deploy to Cloudflare with wrangler, one deployment per network:
the backend as a Worker + D1 + Cron
(pnpm --filter cardano-tessera-backend deploy:preview / deploy:mainnet, after the
one-time D1 setup in backend/server/README.md), the app as static Workers
assets (pnpm --filter tessera-app deploy:preview / deploy:mainnet — each
builds with its committed .env.preview / .env.mainnet and uploads dist/;
see frontend/app/wrangler.toml). The backend/deps submodules
are not needed for any of this; to fetch them anyway:
git submodule update --init --recursive.
Contributions are welcome. Until a CONTRIBUTING.md lands, the basics:
- Open an issue to discuss substantial changes before investing in a PR.
- Keep the build green:
pnpm -r type-check,pnpm -r test, andpnpm format:check(infrontend/app) should all pass (CI runs these on every PR). - Match the existing code style — Prettier is the source of truth for formatting.
The code in this repository is licensed under the Apache License 2.0.