Running a node
One-click deploy
Deploying gives you a Postgres with a volume and a DeDi node pulling
flywheelai/dedi-node, on a
public domain, signing a checkpoint every 30 seconds. It asks you nothing — the
database password, the admin password, the log origin and the public URL are all
derived at deploy time.
Your node mints its own Ed25519 identity key on first boot and keeps it in its own database. Nothing has to be generated beforehand and handed to it.
Which node this is
The button deploys a standalone node in the role sense: its own identity key, its own log, its own origin, nothing above it. It arrives read-only — see below — and in mode 1, meaning nothing independent is checking it yet.
The other four roles are configurations of the same binary, not other products:
| To make it a | set | and read |
|---|---|---|
| witness | DEDI_WITNESS_TARGET_URL + _KEY |
witnessing |
| mirror | DEDI_CRAWL_DOMAINS, and no publisher keys |
file publication |
| replica set member | DEDI_CLUSTER_*, the same key file and origin on every member |
replication |
| child of another node | an enrolment offer from the parent | delegation |
A running node's console renders these for you, with only the fields each one needs: the Add a node tab. Two of them cannot be reached by editing variables on an existing deployment, and it is worth knowing why before you try:
- A replica set is a fixed membership decided at bootstrap. This daemon
bootstraps a Raft configuration and has no way to add a member to a running
cluster, so a set is configured together, once, with
DEDI_CLUSTER_BOOTSTRAP=trueon exactly one member on its first start. - A child is granted its namespace by a parent, so it needs a one-time enrolment offer minted there. It generates its own key on first boot; a parent that ever held that key could forge the child's checkpoints.
The reverse is easy: this node becomes witnessed — mode 2 — when somebody else points a witness at it. Nothing changes here at all, which is the point. Its verifier key and checkpoint are already public.
What you get
Within a couple of minutes:
https://<your-domain>/ the explorer
https://<your-domain>/healthz status, including database reachability
https://<your-domain>/dedi/log/checkpoint a signed checkpoint over your log
https://<your-domain>/.well-known/dedi.index.json your signed publication manifest
The checkpoint's first line is your log's origin, which is your own domain. That is the thing anyone verifying you — or witnessing you — checks against, so it is tied to the deployment rather than to us.
To hand someone your verifier key:
railway logs -s dedi-node | grep -A1 'verifier key'
It serves reads, and accepts no writes
A fresh node is read-only, and that is the intended starting state rather than a
missing step. The write plane only exists once DEDI_PUBLISHER_KEYS is set, and
that key is generated by you:
docker run --rm -v "$PWD:/w" -w /w flywheelai/dedi-node \
pubkeygen -kid my-key -namespace my-namespace
It writes the private key to publisher.key, to keep, and prints the
DEDI_PUBLISHER_KEYS line to set on the service. If this node will serve the
Beckn ONIX registry lookup, also set DEDI_WILDCARD_NAMESPACES
(configuration). Until you do, /admin is not merely locked — it is not routed at
all, and answers 404.
If the node serves Beckn, also set DEDI_WILDCARD_NAMESPACES to the namespaces
allowed to answer the subscribers.beckn.one wildcard lookup and ?domain=
discovery. Left unset on a node with a publisher key, both answer nothing.
DEDI_ADMIN_PASSWORD is already set for you, to a value Railway generated for
your deployment alone. It gates the console the moment your publisher key brings
it into existence, which matters because that moment is exactly when nobody
remembers to go back and add a password. Read it from the Railway variables
panel; it is not written down anywhere else, including here.
Joining a network
A node on its own is tamper-evident to anyone holding its checkpoints. It
becomes tamper-evident to people who are not watching it the moment another node
witnesses it: set DEDI_WITNESS_TARGET_URL (ending in /dedi),
DEDI_WITNESS_TARGET_KEY and DEDI_WITNESS_TARGET_ORIGIN and your node will
poll that one, verify its consistency proofs, and record its verdicts in its own
log, published at /dedi/witness. DEDI_PEERS makes your explorer show the network you
belong to.
See deployment-modes.md for witnessing, ledger anchoring and clustering.
Checking it conforms
The node you just deployed can be measured against the standard by a suite that was not written to flatter it:
docker run --rm -v "$PWD:/work" flywheelai/dedi-conformance:v0.0.1 \
--manifest /work/dedi-conformance.json
It only issues GETs and needs no credentials. See conformance.md.
Changing what the button deploys
The template is generated from a real Railway project, not written as a file, so
the project is the source of truth. scripts/railway_template.py builds that
project reproducibly — read its comments before editing, particularly the reason
every value in it is an interpolation rather than a literal.
scripts/railway_template.py --build
scripts/railway_template.py --generate <project-id>