height 23731 of 23731 on the network. 9 peer(s) connected, 77 in its peer directory; 1027 block(s) followed and 855 pulled in by sync since this follower started. Caught up: this follower holds every block any peer has claimed, so every panel below is the whole chain. Genesis 1cf8655125cc1bbb…, producers genesis.
What this follower is, locally
Following 7 seed(s) — their addresses are not published here, for the reason the peer table gives. Its store is held under its own Cerberus lease — no other node’s store is opened, touched or stopped.
Fork documentation
Everything an operator needs to rehearse a business fork, launch one that can carry value, and run it: every flag and command, the endpoints, the two key paths, the governance arithmetic, what is permanent, what to check before value, and every refusal you may meet.
What a business fork is
A business fork, which SourceLess calls Vanaheim, is a new and independent chain started from the SourceLess software: its own genesis, its own chain id, its own validators and its own assembly. It runs the same code as Asgard, so it has contracts, STR.domains, encrypted storage and STR.talk from its first block. It shares nothing else with Asgard: no validators, no state and no governance. Nothing in Asgard governs it.
One command, node scripts/fork.mjs, builds the genesis, starts the validator set and prints the genesis hash and the endpoints. A fork it starts with derived keys is a rehearsal; a fork started from private production keys is a chain that can carry value.
Fork, or a token on Asgard
Most projects want a token. A ZK13 token on Asgard is one deploy record, inherits the security of the SourceLess validators, and can be replaced by deploying another. A fork is a validator set you operate for the life of the chain, and its genesis cannot be undone.
Fork only when at least one of these is true:
- your asset must be native to the chain, not a balance in a contract;
- your data must not sit on a chain other people read;
- your regulator requires you to operate the validators.
If none is, start from the token template instead.
What you need
- A build of the SourceLess software.
scripts/fork.mjsruns from the source tree and starts nodes from its build, so runnpm ciandnpm run buildfirst. - Node.js 20.11 or later. The version the package declares; the measured runs used Node 22.
- One machine to rehearse on. Every seat of a rehearsal runs as a process on the machine that runs the command, on ports the operating system chooses, all on loopback.
- For a chain that will carry value: production keys made privately with SourceLess, one host per seat, and a public address, a domain name and TLS for the endpoints you publish.
Rehearse a fork
One command starts a rehearsal. The command builder writes it for the values you choose.
node scripts/fork.mjs up --name "Lindworm Freight" --chain-id 606060 --validators 3 \ --allocate treasury:STR:1000000:STR.lindwormtreasury \ --allocate ecosystem:STR:250000 \ --heartbeat-ms 1500
Every flag:
| Flag | What it sets |
|---|---|
--name | The chain's name. Required, with no default. It is in the genesis hash, so it is part of the chain's identity and cannot be changed afterwards. |
--chain-id | A positive integer. The ids of the realms in the register are refused by name: 31337 (dev), 131313 (Midgard), 1313 (Asgard). |
--validators | How many seats the genesis declares, and how many processes start. Required, with no default. This is the starting producer set. |
--allocate | Repeatable: <label>:<token>:<whole amount>[:<STR.domain>]. The first is the treasury and must carry a domain, because an unproved wallet cannot spend. Tokens: STR, CCOS, WSTR, ARSS, STR_USD, ESTR. Whole units; the ledger has 13 decimals and the command does the arithmetic. |
--owner-domain | The STR.domain that holds every genesis node. Defaults to STR. and the slug of the chain name. One genesis has one owner. |
--root | Where the data directories, the genesis and the manifest live. Defaults to .sourceless/forks/<slug>-<chain id> in the source tree. |
--fresh | Deletes the data root first. This throws away the chain in it. |
--foreground | Runs the supervisor in the terminal instead of detaching it. |
--lifetime-hours | The bound after which every node stops itself. Default 168. |
--heartbeat-ms | Queues one real STR.domain allocation every N milliseconds so an otherwise idle chain advances. Default 0, which is off. |
The banner prints the chain, the genesis hash, the register's verdict (no realm matches: this is a fork), the validator set, and for every node its seat and its endpoints.
Run, stop and resume
node scripts/fork.mjs status # what every node holds right now node scripts/fork.mjs seed # give the chain work, so it advances node scripts/fork.mjs down # stop, keeping the chain node scripts/fork.mjs up ... # again: resumes from the durable log node scripts/fork.mjs --help # answers, and starts nothing
- An idle chain that does not advance is not a fault. A producer with an empty queue seals nothing. Use
seed, or start with--heartbeat-ms. seedspends from the treasury. On a rehearsal it proves the treasury, registers four accounts and sends them transfers. On a production fork it refuses, because the treasury key is held by people and the script has none: fund accounts from a wallet that holds a production key, against the fork's RPC.status,seedanddownfind the fork from--root, from--nameand--chain-id, or from the one fork that is there.
The endpoints
| Endpoint | What it is | Who may reach it |
|---|---|---|
rpc | Iris, the public JSON-RPC door, and the one a wallet comes through. POST with named parameters; positional parameters are refused. genesis answers the chain id, the genesis hash and the chain binding; getStatus the height. | Wallets and applications |
ops | Astrolabe, read-only: GET and HEAD only. | Monitoring |
admin (write) | The supervisor's control socket, unauthenticated, and the only write path onto a running node. | Loopback only. Never expose it, on a fork or anywhere else. |
Every port is ephemeral: the command binds no fixed port, so it cannot collide with anything already running. The ports of your run are in the banner and in <data root>/fork.json. Every one is loopback. A published endpoint, a domain name, TLS and a seed list are separate work, and a node refuses to publish a loopback or private address as a seed, because a seed list is read by strangers.
Keys: a rehearsal and a chain that carries value
| Rehearsal: --name --chain-id | Production: --genesis --seat | |
|---|---|---|
| Where the keys come from | Derived by the script from the chain name and id | Made and held privately, never derived |
| Who can seal your blocks | Anybody who knows the chain name and id | Only the holders of the production keys |
| What the banner calls it | DERIVED, a rehearsal chain | DELIVERED |
| What it is for | Testing the start-up path and an application against a chain of your own | A chain anybody may put value on |
A production fork's keys are made with SourceLess before launch. Then node scripts/fork.mjs up --genesis <file> --seat <dir> --seat <dir> … starts the chain from its genesis, one --seat per seat the genesis declares.
- Both keystores on every node are sealed, and nodes run in production mode, so an unsealed validator is refused.
- On the production path the command refuses outright when
SOURCELESS_KEY_PASSPHRASEis not set. - A rehearsal's keys are not custody, and the script says so on every run.
The governance arithmetic
Every genesis seat carries the same voting power, 50. A parameter change passes at 51% of the total, rounded up, so the bar is ceil(seats × 50 × 51 / 100). On a small fork that is very few seats:
| Seats | Total power | Bar | What it means |
|---|---|---|---|
| 3 | 150 | 77 | two seats decide every chain parameter |
| 10 | 500 | 255 | six seats |
| 100 | 5,000 | 2,550 | fifty-one seats |
Compute your bar and publish it. Adding or removing a seat after genesis is a governance action, at a 30-day delay.
What the genesis fixes, and what can change
- Fixed for ever: the chain name and the chain id, both in the genesis hash; the allocations; and everything bound to the hash. Transaction, block and contract signatures bind it, so none replays onto another chain, and the Feeless Pool address is derived from it.
- Changeable by your assembly, each after its delay:
| Parameter | Range | Default | Delay |
|---|---|---|---|
chain.blockTargetSeconds | 1–60 s | 3 s | 7 days |
chain.requiredConfirmations | 4–127 | 12 | 7 days |
governance.votingWindowMs | 1 h – 30 d | 7 d | 30 days |
governance.ordinaryTimelockMs | 7 d – 90 d | 7 d | 30 days |
governance.governanceChangeTimelockMs | 30 d – 365 d | 30 d | 30 days |
Contracts on your fork
Every contract template in this build deploys on a fork. Fork contracts lists them, and each has a draft builder: edit its constants, see the bytecode and the address, download the file.
- The same compiler gives the same bytecode, the same STARW VM charges the same gas, and the same deployer and salt give the same address.
- The state is your fork's alone, and contract state cannot be migrated across a genesis boundary.
- A deploy is signed by an account holding a proved STR.domain on your fork. There is no fee to a producer.
- A deployment of a template still carrying its catalogue examples (an "Example" name, an example ticker) is refused before a passphrase is asked for.
strblk contract template <name> > <name>.ares strblk wallet deploy <name>.ares --dry-run --address <you> strblk wallet deploy <name>.ares --node <your fork's RPC>
Before it signs, the wallet prints the network it is about to sign for: on your fork the realm reads unregistered, and the genesis is your fork's hash. That hash is what the signature binds.
Before your fork carries value
| Condition | If it is skipped |
|---|---|
| A real ZK13 prover is bound | The reference provider refuses to run in production, so the chain does not start. |
| Every allocation and every node has a private production key | Derived keys come from public seeds: anyone can sign as them. |
| Node keys are sealed at rest | Production mode refuses an unsealed validator. |
| The starting validator set is chosen deliberately | A seat short for a month: changing the set takes 30 days. |
| The Althing bar is computed and published | A governance claim your own genesis contradicts. |
requiredConfirmations is at least 4 (the default is 12) | A block called final before anything attested to executing it. The floor of 4 enforces this. |
| A restore from a snapshot has been rehearsed and reproduces the state root | A snapshot that restores to a different root is a fork waiting for a restart. down then up rehearses a restart from the log; the snapshot restore is yours to run. |
| An endpoint, a name and a seed list anybody can reach | A fork nobody can dial is a fork with one operator. |
What a fork does not get
- No shared security. Your blocks are sealed by your seats and by nobody else. The SourceLess validators do not see your chain, hold no key on it, and could not help it if every one of your nodes went down.
- No bridge. There is no path for value to move between your chain and SourceLess, or anything else. A fork is created fail-closed: it mints no $TR and refuses every collateral record.
- Your own validator set to recruit. The genesis set is your whole producing capacity on day one, and adding a seat afterwards is a governance action at the 30-day delay.
- Your own custody to perform. A rehearsal fork's keys can be derived by anyone who knows its name and id, so a fork that carries value has its keys made and held privately before it launches.
- No real ZK13 prover. The reference provider refuses to run in production until a real prover is bound.
- No migration path. Contract state cannot be moved onto a fork or off it across a genesis boundary.
- No endpoint anybody else can reach, out of the box. Every port the fork command opens is loopback; a published endpoint, a domain name, TLS and a seed list are separate work.
Refusals you may meet
| The command says | Why, and what to do |
|---|---|
| the chain id is a realm in the register | A fork answering to a realm's id can be mistaken for it by anything that checks an id and not a hash. Pick another id. |
| this is the development genesis, whole | Every key in it derives from a public seed. Build your own with --name and --chain-id. |
| this genesis is a realm | The genesis hashes to a realm's pin. A fork must have a genesis of its own. |
| the name has no letters or digits | The name becomes the owner domain and the directory. Pick a name a person would recognise. |
| no --allocate | A chain with no allocation has nothing anyone can spend. Add a treasury allocation with a domain. |
| --allocate is not label:token:whole[:STR.domain] | Labels are lower case, digits and hyphens; the token is one of the six; the amount is a whole number. |
| no SOURCELESS_KEY_PASSPHRASE | On the production path the sealed keystores need it. Set it in the environment the command runs in. |
| seed on a custody fork | The treasury key is held by people. Fund accounts from a wallet that holds one. |
| the height does not move | Not a refusal: an idle chain seals nothing. Run seed or use --heartbeat-ms. |
Where to go next
- The fork stack: what else runs on a fork beside its seats: the three node types, Praeco discovery, hostless storage, STR.talk and the door, each with what works today.
- Realms: the register a fork is checked against.
- Business forks: the short version of this page.