Contributing
zantiflow is a polyglot monorepo: TypeScript (backend, web, shared packages) via pnpm, a Rust plugin via Cargo, and Python bots via pip.
Layout
Section titled “Layout”apps/ plugin (Rust) backend (TS) web (Next.js) discord-bot telegram-bot (Python)packages/ protocol oauth* notify-protocoldocs/ this site (Starlight)deploy/ compose + Caddyfile + .env.exampleadrs/ FINDINGS.md plans/Build & test
Section titled “Build & test”pnpm installpnpm -r build && pnpm -r test # TypeScript (integration tests need Docker/Podman)cargo test -p zantiflow-plugin # plugin (native) + cargo build --target wasm32-wasip1pytest packages/notify-protocol apps/discord-bot apps/telegram-bot # botspnpm lint && pnpm format:check # + cargo fmt/clippy, ruffPlugin dev loop: a stable token across rebuilds
Section titled “Plugin dev loop: a stable token across rebuilds”Zellij namespaces each plugin’s /cache and /data storage by the plugin’s URL. The usual
rebuild trick — bumping the wasm filename so start-or-reload-plugin picks up the fresh binary —
therefore hands the plugin a brand-new, empty /cache every time. Device pairing persists its minted
ingest token in /cache, so each filename bump wipes it and the plugin drops back to showing a
pairing code.
Skip pairing entirely by passing the token in the launch command. A config token is the
plugin’s top-priority source: it wins over the cached/paired token and short-circuits pairing, and
because it lives in the command (not per-URL storage) it survives every path bump.
zellij action start-or-reload-plugin "file:/repos/zantiflow7.wasm" \ -c "token=ztf_…,server_url=http://localhost:4000,machine_name=alias:my-vm,full=true,pane_output=true"Mint a local dev token against the dev DB (an infinite, revocable ingest token bound to a throwaway
dev/local account):
pnpm --filter @zantiflow/backend db:up # start the dev MariaDB (host port 3308)cd apps/backendpnpm exec prisma migrate deploy # first run onlypnpm exec tsx --env-file=.env scripts/dev-mint-token.ts # prints ztf_… on the last lineThat token is a dev-DB-only secret — fine to keep in a shell alias or script on your dev box. In
production the same token= slot is filled by a token you copy once from the dashboard, or you leave
token out and pair via zantiflow.com/pair (see Plugin: getting started).
Testing philosophy (ADR-0014)
Section titled “Testing philosophy (ADR-0014)”Unit + BDD + integration + Playwright, behind mockable ports. Externals (Google, Web Push,
Discord/Telegram) are mocked; MariaDB is real via testcontainers. The plugin’s logic is tested
natively behind a HostPort trait; the real Zellij API is exercised only in a throwaway session.
Proposing changes
Section titled “Proposing changes”If your change involves a decision the existing ADRs don’t settle, add a new ADR alongside the code. One decision, one ADR.