@mcsherrylabs/pi-meerkat 0.7.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -33
- package/dist/installer/committee-init.sh +117 -0
- package/dist/installer/committee.conf +102 -0
- package/dist/installer/genesis.devnet.conf +92 -0
- package/dist/installer/node-join.sh +235 -0
- package/dist/installer/node.conf +238 -0
- package/dist/installer/node.devnet.conf +238 -0
- package/dist/installer/runtime.json +1 -0
- package/dist/installer-runtime/dist/scripts/recover-owner.js +15 -0
- package/dist/installer-runtime/dist/scripts/subscriptions-init.js +35 -0
- package/dist/installer-runtime/dist/scripts/versions.mjs +86 -0
- package/dist/installer-runtime/dist/scripts/wait-for-chain.js +90 -0
- package/dist/installer-runtime/dist/src/announcements.js +448 -0
- package/dist/installer-runtime/dist/src/app.js +552 -0
- package/dist/installer-runtime/dist/src/chainfetch.js +87 -0
- package/dist/installer-runtime/dist/src/env.js +437 -0
- package/dist/installer-runtime/dist/src/registry/announce.js +77 -0
- package/dist/installer-runtime/dist/src/registry/announcement.js +141 -0
- package/dist/installer-runtime/dist/src/registry/dependencies.js +28 -0
- package/dist/installer-runtime/dist/src/registry/detail.js +87 -0
- package/dist/installer-runtime/dist/src/registry/drainer.js +131 -0
- package/dist/installer-runtime/dist/src/registry/http.js +194 -0
- package/dist/installer-runtime/dist/src/registry/index.js +61 -0
- package/dist/installer-runtime/dist/src/registry/lease.js +35 -0
- package/dist/installer-runtime/dist/src/registry/ownership.js +31 -0
- package/dist/installer-runtime/dist/src/registry/provision.js +225 -0
- package/dist/installer-runtime/dist/src/registry/store.js +242 -0
- package/dist/installer-runtime/dist/src/registry/sweep.js +9 -0
- package/dist/installer-runtime/dist/src/registry/worker-module.js +87 -0
- package/dist/installer-runtime/dist/src/runtime.js +192 -0
- package/dist/installer-runtime/dist/src/store.js +138 -0
- package/dist/installer-runtime/package.json +1 -0
- package/dist/installer.d.ts +401 -0
- package/dist/installer.d.ts.map +1 -0
- package/dist/installer.js +446 -0
- package/dist/installer.js.map +1 -0
- package/dist/mcp-announce.d.ts +18 -0
- package/dist/mcp-announce.d.ts.map +1 -1
- package/dist/mcp-announce.js +18 -4
- package/dist/mcp-announce.js.map +1 -1
- package/dist/mcp-cli.d.ts.map +1 -1
- package/dist/mcp-cli.js +41 -5
- package/dist/mcp-cli.js.map +1 -1
- package/dist/mcp-config.d.ts +1 -0
- package/dist/mcp-config.d.ts.map +1 -1
- package/dist/mcp-config.js +2 -1
- package/dist/mcp-config.js.map +1 -1
- package/dist/mcp-upstream.d.ts +1 -1
- package/dist/mcp-upstream.d.ts.map +1 -1
- package/dist/mcp-upstream.js +7 -2
- package/dist/mcp-upstream.js.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +8 -4
- package/dist/registry.js.map +1 -1
- package/dist/tools.js +4 -4
- package/dist/tools.js.map +1 -1
- package/installer/Committee.Dockerfile +6 -0
- package/installer/Node.Dockerfile +10 -0
- package/installer/Registry.Dockerfile +13 -0
- package/installer/runtime-control.mjs +335 -0
- package/installer/session-files.mjs +16 -0
- package/package.json +8 -7
- package/skills/channel-subscription/SKILL.md +3 -3
package/README.md
CHANGED
|
@@ -5,28 +5,116 @@ Meerkat tools plus every tool dynamically discovered from the private
|
|
|
5
5
|
`@mcsherrylabs/evolver-mcp@0.15.0` child. The released upstream baseline is 65 tools, so healthy
|
|
6
6
|
discovery currently returns 72. The count is a comparison baseline, not a routing allowlist.
|
|
7
7
|
|
|
8
|
-
**Version 0.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
**Version 0.9.0 makes DevNet the default install target** (`install` installs against the public
|
|
9
|
+
DevNet; `install --local` creates a private local chain). Version 0.8.0 added the
|
|
10
|
+
zero-required-parameter installer (local chain only). Version 0.7.0 introduced the
|
|
11
|
+
facade but required an existing node, registry, identity and key. Version 0.6.0 is the old
|
|
12
|
+
Pi/local-signing package. Always use an exact published version, not npm `latest`.
|
|
12
13
|
|
|
13
|
-
## Install
|
|
14
|
+
## Install the complete experience
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
Prerequisites in the environment where your MCP harness runs:
|
|
17
|
+
|
|
18
|
+
- Node.js **22 or newer**, with `npm` and `npx` on `PATH`.
|
|
19
|
+
- A reachable Docker Engine and **Docker Compose v2** (`docker version`, `docker compose version`).
|
|
20
|
+
A normal local engine or a separately hosted Docker daemon/context both work. Existing Docker
|
|
21
|
+
authentication/TLS settings are respected; no new credentials are requested for public images.
|
|
22
|
+
- An MCP harness with stdio support. The installer prints registration JSON; it does not edit
|
|
23
|
+
the harness or silently replace an existing registration.
|
|
24
|
+
|
|
25
|
+
### 1. Install
|
|
18
26
|
|
|
19
27
|
```bash
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export PATH="$HOME/.local/share/meerkat-mcp/0.7.0/node_modules/.bin:$PATH"
|
|
28
|
+
npx --yes @mcsherrylabs/pi-meerkat@0.9.0 install # public DevNet (default)
|
|
29
|
+
npx --yes @mcsherrylabs/pi-meerkat@0.9.0 install --local # private local chain
|
|
23
30
|
```
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
No identity, key, endpoint, registry, chain ID, channel, port, GID, path, environment variables
|
|
33
|
+
or paid model service is required.
|
|
34
|
+
|
|
35
|
+
**Default (DevNet)** installs a single non-genesis follower node against the public DevNet, syncing
|
|
36
|
+
its shared chain, and a Meerkat registry that reads and indexes the shared, pinned
|
|
37
|
+
`ce.meerkat.announcements` channel (owned by the pinned `evolvermeerkatA483344` operator). That
|
|
38
|
+
registry runs **read-only**: it never claims, signs or submits with any identity. DevNet catch-up
|
|
39
|
+
syncs from block 0 (~14k blocks at the time of release) and is paced by the registry's own cursor
|
|
40
|
+
drain, so the first install can take a while; it reports progress and fails only if sync stalls.
|
|
41
|
+
|
|
42
|
+
A DevNet install **permanently claims two DevNet identities**, both funded by the public DevNet
|
|
43
|
+
faucet: its node (`meerkatnode<id>`) and your agent (`meerkatuser<id>`), which signs your
|
|
44
|
+
announcements. Names on DevNet cannot be reused. Re-running install never claims again. DevNet mode
|
|
45
|
+
needs **outbound internet access** from Docker (the node dials DevNet's front nodes, and the faucet
|
|
46
|
+
is reached over HTTPS); it still publishes no host ports.
|
|
47
|
+
|
|
48
|
+
**`--local`** creates an **independent private local chain** — an owned consensus node, a separate
|
|
49
|
+
processing node, and a Meerkat registry/content/inbox service. The agent's own identity
|
|
50
|
+
(`meerkatuser<id>`) owns the install's private `meerkat.<id>.announcements` channel (claimed once,
|
|
51
|
+
idempotently), and nothing touches DevNet. In either mode the registry claims no identity, and the
|
|
52
|
+
installer never inspects unrelated keys or changes another deployment. The first install downloads public pinned artifacts and
|
|
53
|
+
builds the wrapper images, so allow time for Docker/npm downloads. Failed
|
|
54
|
+
preflight/build/readiness exits nonzero with a reason. Each mode keeps its own state directory
|
|
55
|
+
(`devnet/` or `local/` under your XDG state directory); 0.8.0 state is never touched.
|
|
28
56
|
|
|
29
|
-
|
|
57
|
+
### 2. Register one server
|
|
58
|
+
|
|
59
|
+
Use the one `registration.mcpServers.meerkat` entry printed by installation. For Claude Code,
|
|
60
|
+
if you do not already have a registration named `meerkat`, the equivalent command is:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
claude mcp add --scope user --transport stdio meerkat -- \
|
|
64
|
+
npx --yes @mcsherrylabs/pi-meerkat@0.9.0 serve
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Add `--local` after `serve` if you installed with `install --local`.
|
|
68
|
+
|
|
69
|
+
Do not remove or replace an unrelated `meerkat` registration. Choose a different harness
|
|
70
|
+
registration name for this entry if necessary; its underlying saved deployment remains the same.
|
|
71
|
+
Other MCP clients can add the printed stdio JSON entry directly. Do **not** register an Evolver
|
|
72
|
+
child separately. Restart the harness connection, inspect its tools, and begin with
|
|
73
|
+
`meerkat_summary`, `meerkat_services`, `meerkat_inbox` and `evolver_chain_height`.
|
|
74
|
+
|
|
75
|
+
`serve` finds the installer's saved state automatically (default `devnet`, `--local` for the
|
|
76
|
+
private chain); there is no `--config` or environment ceremony. The public package launcher is
|
|
77
|
+
pinned and survives npm-cache cleanup. Each client
|
|
78
|
+
gets its **own facade and exact Evolver child/session/store** inside the owned runtime container,
|
|
79
|
+
connected through Docker exec stdio. Node/content/registry HTTP stays on the private owned
|
|
80
|
+
Docker network, with **no host-published ports**, so a separate Docker daemon needs neither
|
|
81
|
+
host-path sharing nor a public read port.
|
|
82
|
+
Stdout carries MCP protocol only. Closing the connection or signalling the launcher awaits the
|
|
83
|
+
exact remote facade and its private child, not the shared registry or another client's session.
|
|
84
|
+
|
|
85
|
+
### Resume and ownership
|
|
86
|
+
|
|
87
|
+
Run the **same install command** again to resume the same installation. Names, identity/key
|
|
88
|
+
references, internal service ports, daemon identity, real genesis/node binding and ownership are saved
|
|
89
|
+
privately under the operator's XDG state directory (or normal home state directory). Services
|
|
90
|
+
use separately owned Docker volumes for consensus data, processing-node data, public genesis,
|
|
91
|
+
registry/app state, subscriptions and announcer state. Consensus and consumer-paced processing
|
|
92
|
+
are separate roles required by the released node. The follower receives only the **public** chain
|
|
93
|
+
specification, never the consensus node's private volume. The released Evolver bootstrap alone
|
|
94
|
+
creates/loads announcer signing keys; the facade,
|
|
95
|
+
installer and output carry only references. No key bytes are copied to the harness.
|
|
96
|
+
|
|
97
|
+
Rerun does not initialize subscriptions again, regenerate genesis, rotate keys, change authority,
|
|
98
|
+
force-claim/unclaim, adopt another daemon's resources or recover an owner lock. A missing valued
|
|
99
|
+
volume, conflicting ownership, changed daemon/binding, interrupted initializer or stale owner lock
|
|
100
|
+
refuses explicitly and retains state for operator inspection. There is no automatic reset,
|
|
101
|
+
force recovery, Docker prune, broad restart or harness-registration mutation. `serve` never
|
|
102
|
+
creates or restarts services. An intentional **install** invocation may start its own stopped
|
|
103
|
+
services; unrelated services are never touched. Automatic cross-version migration is not provided:
|
|
104
|
+
resume with the saved pinned version.
|
|
105
|
+
|
|
106
|
+
The installer enables the complete registry/read/announce experience, not every optional upstream
|
|
107
|
+
capability. Generic Evolver subscription-control and other optional capability tools can remain
|
|
108
|
+
unconfigured and refuse clearly; no Docker socket or private node-control API is exposed to the
|
|
109
|
+
registry/facade. The real tool catalog baseline is 65 upstream + 7 local, **not a promise that each
|
|
110
|
+
optional capability is configured**.
|
|
111
|
+
|
|
112
|
+
## Advanced: attach an existing node
|
|
113
|
+
|
|
114
|
+
If you already operate a node, registry and keys, you may install this exact package normally
|
|
115
|
+
and use `meerkat-mcp setup` instead. This is an alternative to the automatic installer, not a
|
|
116
|
+
prerequisite for it. Keep your installed package on a persistent user-owned PATH, not an
|
|
117
|
+
npm-cache-internal pathname.
|
|
30
118
|
|
|
31
119
|
Setup records **references**, not key bytes. Select an existing node endpoint, separate content
|
|
32
120
|
endpoint, identity, existing key file, registry read origin, explicit genesis/chain ID and node ID,
|
|
@@ -70,24 +158,12 @@ Pi adapter, or separately managed child is required. Direct launch uses the same
|
|
|
70
158
|
meerkat-mcp --config "$MEERKAT_CONFIG"
|
|
71
159
|
```
|
|
72
160
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
claude mcp add --scope user --transport stdio meerkat -- \
|
|
80
|
-
"$(command -v node)" \
|
|
81
|
-
"$HOME/.local/share/meerkat-mcp/0.7.0/node_modules/@mcsherrylabs/pi-meerkat/dist/mcp-cli.js" \
|
|
82
|
-
--config "$MEERKAT_CONFIG"
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
`--scope user` makes this operator registration available across your projects. Use
|
|
86
|
-
`--scope local` instead to restrict the initial test to the current project. Start a fresh
|
|
87
|
-
Claude Code session and run `/mcp` to inspect the connection and its tools. Do not register the
|
|
88
|
-
private Evolver child separately. Begin with `meerkat_summary`, a small `meerkat_services`
|
|
89
|
-
query, `meerkat_inbox`, and the upstream read-only `evolver_chain_height`; do not approve
|
|
90
|
-
announcement/sign/submit calls until you deliberately choose to exercise a write.
|
|
161
|
+
For advanced attach, register the exact persistent installed entrypoint/config returned by
|
|
162
|
+
setup; do not point a client at an npm-cache-internal file. The automatic installer's pinned
|
|
163
|
+
`serve` registration above is the recommended route for a virgin harness. In Claude Code,
|
|
164
|
+
`--scope user` makes the operator registration available across projects; `--scope local`
|
|
165
|
+
limits it to the current project. Start a fresh session and use `/mcp` to inspect the connection.
|
|
166
|
+
Do not approve announcement/sign/submit calls until you deliberately choose to exercise a write.
|
|
91
167
|
|
|
92
168
|
The stdio connection uses stdout exclusively for MCP messages. Diagnostics go to stderr. The
|
|
93
169
|
operator-selected node/content/trust/identity/key/channel inputs are captured at facade startup;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# node entrypoint: host keys + genesis on first boot, then exec the node.
|
|
3
|
+
#
|
|
4
|
+
# On FIRST boot it generates this node's key material ON THE HOST (never in the
|
|
5
|
+
# image or the repo), renders a one-node genesis committee from the derived public
|
|
6
|
+
# keys, and persists everything into the /data volume. On every subsequent boot it
|
|
7
|
+
# REUSES the same keys + genesis so the chain continues — the genesis committee must
|
|
8
|
+
# stay byte-stable or the chain identity diverges.
|
|
9
|
+
#
|
|
10
|
+
# Two secrets, handled identically (spec 101 — both are host-only secrets):
|
|
11
|
+
# - Ed25519 node-identity key -> ce.evolver.node.private-key (EVOLVER_PRIVATE_KEY)
|
|
12
|
+
# - BLS consensus key (IKM) -> ce.evolver.node.bls-secret (EVOLVER_BLS_SEED)
|
|
13
|
+
# Their public keys go into genesis (public-key / bls-public-key).
|
|
14
|
+
set -eu
|
|
15
|
+
|
|
16
|
+
# Deliberately NOT derived from the app name. This id is written into the genesis
|
|
17
|
+
# file on the volume at FIRST BOOT, so changing it on an existing chain would launch
|
|
18
|
+
# the node under a name absent from its own genesis. Keeping it neutral means
|
|
19
|
+
# `npm run rename` never has to touch this file and cannot leave a previous project's
|
|
20
|
+
# name behind — the template used to default to `tmplnode0`, so every app built from
|
|
21
|
+
# it had a node introducing itself by the TEMPLATE's name, in its logs and in its
|
|
22
|
+
# published deployment artifact.
|
|
23
|
+
NODE_ID="${NODE_ID:-node0}"
|
|
24
|
+
KEYS=/data/keys
|
|
25
|
+
GENESIS=/data/genesis.gen.conf
|
|
26
|
+
ED_KEY="$KEYS/ed25519.key" # base64 PKCS#8 Ed25519 private
|
|
27
|
+
BLS_KEY="$KEYS/bls.key" # base64 BLS IKM (>=32 bytes)
|
|
28
|
+
|
|
29
|
+
if [ -f "$ED_KEY" ] || [ -f "$BLS_KEY" ] || [ -f "$GENESIS" ]; then
|
|
30
|
+
[ -s "$ED_KEY" ] && [ -s "$BLS_KEY" ] && [ -s "$GENESIS" ] || {
|
|
31
|
+
echo '[node-init] incomplete owned keys/genesis; refusing regeneration' >&2; exit 1;
|
|
32
|
+
}
|
|
33
|
+
fi
|
|
34
|
+
if [ ! -f "$ED_KEY" ]; then
|
|
35
|
+
echo "[node-init] first boot: generating host key material for '$NODE_ID'"
|
|
36
|
+
mkdir -p "$KEYS"; chmod 700 "$KEYS"
|
|
37
|
+
|
|
38
|
+
# Ed25519 identity keypair — PKCS#8 private + X.509 public, base64 DER.
|
|
39
|
+
tmp="$(mktemp)"
|
|
40
|
+
openssl genpkey -algorithm ed25519 -outform DER -out "$tmp" 2>/dev/null
|
|
41
|
+
ED_PRIV="$(base64 -w0 < "$tmp")"
|
|
42
|
+
ED_PUB="$(openssl pkey -inform DER -in "$tmp" -pubout -outform DER 2>/dev/null | base64 -w0)"
|
|
43
|
+
rm -f "$tmp"
|
|
44
|
+
|
|
45
|
+
# BLS consensus keypair — random IKM + derived pubkey, via the evolver keygen.
|
|
46
|
+
BLS_OUT="$(java -cp "/opt/evolver/lib/*" ce.evolver.crypto.keys.BLSKeyGenerator)"
|
|
47
|
+
BLS_SECRET="$(echo "$BLS_OUT" | awk '/^bls-secret:/{print $2}')"
|
|
48
|
+
BLS_PUB="$(echo "$BLS_OUT" | awk '/^bls-public-key:/{print $2}')"
|
|
49
|
+
|
|
50
|
+
if [ -z "$ED_PRIV" ] || [ -z "$ED_PUB" ] || [ -z "$BLS_SECRET" ] || [ -z "$BLS_PUB" ]; then
|
|
51
|
+
echo "[node-init] FATAL: key generation produced empty output" >&2
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Persist secrets — host-only, mode 600.
|
|
56
|
+
printf '%s' "$ED_PRIV" > "$ED_KEY"; chmod 600 "$ED_KEY"
|
|
57
|
+
printf '%s' "$BLS_SECRET" > "$BLS_KEY"; chmod 600 "$BLS_KEY"
|
|
58
|
+
|
|
59
|
+
# Render the chain spec (089): committee PUBLIC keys + the chain-wide consensus
|
|
60
|
+
# parameters. This exact file is published verbatim to every follower / app node
|
|
61
|
+
# (102 follower-join: the genesis file IS the chain, byte-identical everywhere) and
|
|
62
|
+
# its canonical hash is pinned as the genesis block id (ce.evolver.genesis-file in
|
|
63
|
+
# node.conf). 103: the host is the ROUTABLE public address — followers dial the
|
|
64
|
+
# committee at exactly this host:port. Chain-wide params live HERE, not in node.conf,
|
|
65
|
+
# so joiners inherit them automatically — quiescence especially (FR-019: a joiner
|
|
66
|
+
# with different quiescence would time out the deliberately-lingering leader).
|
|
67
|
+
PUBLIC_HOST="${PUBLIC_HOST:-localhost}"
|
|
68
|
+
cat > "$GENESIS" <<EOF
|
|
69
|
+
ce.evolver.genesis.nodes = [
|
|
70
|
+
{
|
|
71
|
+
node-id = "$NODE_ID"
|
|
72
|
+
host = "$PUBLIC_HOST"
|
|
73
|
+
port = 7000
|
|
74
|
+
public-key = "$ED_PUB"
|
|
75
|
+
bls-public-key = "$BLS_PUB"
|
|
76
|
+
quorum-weight = 1000
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
ce.evolver.consensus {
|
|
81
|
+
n-nodes = 1
|
|
82
|
+
block-time-ms = 600
|
|
83
|
+
round-timeout-ms = 1500
|
|
84
|
+
block-reward-tokens = 10000 # generous dev reward: the faucet (5000/identity) is funded from the FIRST blocks, even under quiescence
|
|
85
|
+
snapshot-interval-blocks = 0 # single-node: no snapshot cadence; joiners sync from block 0
|
|
86
|
+
identity-sponsorship-cost = 100
|
|
87
|
+
|
|
88
|
+
# Low idle disk-write: after 3 empty blocks the leader lingers on a 5-MINUTE heartbeat
|
|
89
|
+
# instead of the 600ms cadence (#31: idle-heartbeat-ms > block-time-ms activates quiescence);
|
|
90
|
+
# wake-on-work (node-local, always on) still proposes immediately on incoming txs.
|
|
91
|
+
# CHAIN-WIDE — identical on every node by construction.
|
|
92
|
+
quiescence {
|
|
93
|
+
idle-heartbeat-ms = 300000
|
|
94
|
+
idle-after-empty-rounds = 3
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
EOF
|
|
98
|
+
echo "[node-init] generated keys (secrets in $KEYS, mode 600) + chain spec (host=$PUBLIC_HOST)"
|
|
99
|
+
echo "[node-init] PUBLISH the chain spec (public keys + chain-wide config): docker compose cp node:/data/genesis.gen.conf ."
|
|
100
|
+
else
|
|
101
|
+
echo "[node-init] existing keys found in $KEYS — reusing (chain continues)"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Secrets reach the node via env (HOCON ${?...} substitution in node.conf).
|
|
105
|
+
EVOLVER_PRIVATE_KEY="$(cat "$ED_KEY")"; export EVOLVER_PRIVATE_KEY
|
|
106
|
+
EVOLVER_BLS_SEED="$(cat "$BLS_KEY")"; export EVOLVER_BLS_SEED
|
|
107
|
+
mkdir -p /data/chain
|
|
108
|
+
|
|
109
|
+
echo "[node-init] launching evolver node '$NODE_ID'"
|
|
110
|
+
if [ -f /public-genesis/genesis.conf ]; then
|
|
111
|
+
cmp -s "$GENESIS" /public-genesis/genesis.conf || { echo '[node-init] public genesis differs; refusing replacement' >&2; exit 1; }
|
|
112
|
+
else
|
|
113
|
+
cp "$GENESIS" /public-genesis/genesis.pending
|
|
114
|
+
chmod 640 /public-genesis/genesis.pending
|
|
115
|
+
mv /public-genesis/genesis.pending /public-genesis/genesis.conf
|
|
116
|
+
fi
|
|
117
|
+
exec /opt/evolver/bin/evolver -Dconfig.file=/config/node.conf "$NODE_ID"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Single-node full-quorum, quiescent. The chain spec (committee with this
|
|
2
|
+
# node's generated public keys + ALL chain-wide consensus params, incl. quiescence) is
|
|
3
|
+
# rendered at first boot by node-init.sh into /data/genesis.gen.conf and included
|
|
4
|
+
# here — this file holds only NODE-LOCAL config. Secrets arrive via env (never in this
|
|
5
|
+
# file).
|
|
6
|
+
include file("/data/genesis.gen.conf")
|
|
7
|
+
|
|
8
|
+
# 089 chain-spec pin: the canonical hash of the genesis file IS the chain's genesis
|
|
9
|
+
# block id. Joiners set this to their verbatim copy — any drift refuses to boot.
|
|
10
|
+
ce.evolver.genesis-file = "/data/genesis.gen.conf"
|
|
11
|
+
|
|
12
|
+
ce.evolver.node {
|
|
13
|
+
private-key = ${?EVOLVER_PRIVATE_KEY} # Ed25519 PKCS#8 (host secret, from node-init.sh)
|
|
14
|
+
bls-secret = ${?EVOLVER_BLS_SEED} # BLS IKM (host secret, from node-init.sh)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
evolver.peer-discovery {
|
|
18
|
+
# What this node ADVERTISES about ITSELF to peers — not somewhere it connects to.
|
|
19
|
+
# This chain is standalone (one node, its own genesis, no seeds) so nothing reads
|
|
20
|
+
# these; localhost is used because the alternative is advertising an address that
|
|
21
|
+
# is not this node. They named devnet.mcsherrylabs.com purely because this config
|
|
22
|
+
# was copied from that deployment. Joining a real chain means setting them to the
|
|
23
|
+
# ROUTABLE p2p transport (raw TCP mTLS :7000) and HTTPS API your peers can dial.
|
|
24
|
+
# 163 — `host:port`, no scheme: the transport is raw TCP + mTLS and never was a URL.
|
|
25
|
+
transport-address = "committee:7000"
|
|
26
|
+
# No `peer-url`: this chain is one node, so nothing fetches content from it, and the
|
|
27
|
+
# plain-HTTP :8440 above could not serve a peer anyway — a content fetch carries the
|
|
28
|
+
# caller's client certificate and needs the node's OWN TLS listener. Joining a real
|
|
29
|
+
# chain means setting `transport-address` to the routable :7000 and `peer-url` to this
|
|
30
|
+
# node's https listener (never a reverse proxy: it presents no client certificate).
|
|
31
|
+
gossip-interval-seconds = 60
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Chain-wide consensus params (n-nodes, block-time, quiescence, ...) come from the
|
|
35
|
+
# included chain spec — do NOT set them here, they must stay byte-identical on every
|
|
36
|
+
# node. Only node-local knobs below.
|
|
37
|
+
ce.evolver.consensus {
|
|
38
|
+
validate-chain-on-startup = false
|
|
39
|
+
|
|
40
|
+
# Reactivity (node-local, always on since #28): on incoming work the leader proposes
|
|
41
|
+
# IMMEDIATELY rather than waiting out the (5-minute) idle heartbeat — tx latency stays
|
|
42
|
+
# sub-second despite slow idle.
|
|
43
|
+
wake-on-work {
|
|
44
|
+
forward-watermark = 16
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
data.base = "/data/chain"
|
|
49
|
+
state.snapshots = "snapshots"
|
|
50
|
+
# PLAIN HTTP on 8440 for everything that TALKS to this node, deliberately. The HTTPS
|
|
51
|
+
# listener presents the node's IDENTITY certificate — self-signed, and valid for ~13
|
|
52
|
+
# MINUTES, because it is a peer authentication credential and not a web certificate. No
|
|
53
|
+
# browser can be made happy with that: an exception you click through expires before you
|
|
54
|
+
# finish reading the page. Tolerating it in code means NODE_TLS_REJECT_UNAUTHORIZED=0,
|
|
55
|
+
# which disables verification PROCESS-WIDE for every host — a bad habit to ship in a
|
|
56
|
+
# template.
|
|
57
|
+
#
|
|
58
|
+
# TLS on 8441 for PEERS, because the content cache needs somewhere mTLS to live: its peer
|
|
59
|
+
# route identifies callers by client certificate and the node refuses to start content
|
|
60
|
+
# without a TLS listener. Before evolver v0.16.0 those were the same listener, so
|
|
61
|
+
# "hold documents" and "put every client on an unpinnable certificate" were one decision.
|
|
62
|
+
# They are not, and this is what that separation looks like in a deployment.
|
|
63
|
+
http.port = 8440
|
|
64
|
+
# ⚠️ REQUIRED since evolver #131: the plain API listener now defaults to 127.0.0.1. The app
|
|
65
|
+
# container reaches this node over the CONTAINER NETWORK, which is not loopback from the
|
|
66
|
+
# node's point of view — without this line the app gets connection refused and the node
|
|
67
|
+
# logs nothing unusual. "0.0.0.0" is what this deployment effectively had before.
|
|
68
|
+
http.bind-host = "0.0.0.0"
|
|
69
|
+
https.port = 8441
|
|
70
|
+
evolver.grpc.port = 9000
|
|
71
|
+
|
|
72
|
+
# 139 — CONTENT, so a detail document announced on this chain can actually be fetched.
|
|
73
|
+
#
|
|
74
|
+
# `bind-host` is what lets a SIBLING CONTAINER reach the publish/read route: it is
|
|
75
|
+
# authorized by what can reach it, and the default (loopback) means "inside this node's
|
|
76
|
+
# container and nowhere else". 0.0.0.0 here says: this compose project's network is the
|
|
77
|
+
# boundary. It holds because the port below is published to 127.0.0.1 only.
|
|
78
|
+
ce.evolver.content {
|
|
79
|
+
enabled = true
|
|
80
|
+
http-port = 8450
|
|
81
|
+
bind-host = "0.0.0.0"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
chainDb {
|
|
85
|
+
datasource {
|
|
86
|
+
driver = "org.hsqldb.jdbc.JDBCDriver"
|
|
87
|
+
user = "sa"
|
|
88
|
+
pass = ""
|
|
89
|
+
maxPoolSize = 5
|
|
90
|
+
connectionProperties = ""
|
|
91
|
+
cachePrepStmts = true
|
|
92
|
+
prepStmtCacheSize = 250
|
|
93
|
+
prepStmtCacheSqlLimit = 2048
|
|
94
|
+
connectionTimeout = 5000
|
|
95
|
+
useServerPrepStmts = false
|
|
96
|
+
transactionIsolationLevel = "TRANSACTION_READ_COMMITTED"
|
|
97
|
+
}
|
|
98
|
+
viewCachesSize = 50
|
|
99
|
+
useShutdownHook = false
|
|
100
|
+
freeBlobsEarly = false
|
|
101
|
+
}
|
|
102
|
+
ce.evolver.execution.enabled = false
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# devnet genesis — the committee, plus SEED NODES (170).
|
|
2
|
+
#
|
|
3
|
+
# ⚠️ THIS FILE IS THE CHAIN'S IDENTITY. Its canonical hash is the genesis block id, so any edit
|
|
4
|
+
# here produces a DIFFERENT CHAIN. Every node must hold a byte-identical copy; a node with an
|
|
5
|
+
# older one does not disagree politely, it cannot join at all.
|
|
6
|
+
#
|
|
7
|
+
# ── Why the seed entries exist (170, issue #121) ────────────────────────────────────────────
|
|
8
|
+
# A node starting with an empty volume can only learn a peer's ADDRESS from this file, and can
|
|
9
|
+
# only VERIFY a peer whose key is already in its chain state — which, before it has synced, is
|
|
10
|
+
# also only this file. Both roads lead here. Before 170 this file named one node, the committee,
|
|
11
|
+
# whose `allowed-inbound` is deliberately full, so a newcomer could reach nothing it was able to
|
|
12
|
+
# trust and nothing it was able to trust would accept it. No configuration could fix that.
|
|
13
|
+
#
|
|
14
|
+
# A `quorum-weight = 0` entry is present-and-trusted WITHOUT being on the committee: it is seeded
|
|
15
|
+
# as an identity, its Ed25519 key becomes verifiable, and its address becomes dialable — while
|
|
16
|
+
# `seedGenesis` keeps it out of the quorum store and leader rotation filters it out of the
|
|
17
|
+
# proposer set. That is the whole mechanism; no new key, no new trust path.
|
|
18
|
+
#
|
|
19
|
+
# ⚠️ The two front nodes keep the names they already had. They were NOT in genesis before, so a
|
|
20
|
+
# chain rebuild would have reassigned their names and invalidated every pin across the estate and
|
|
21
|
+
# the published installer. Pinning them here makes the names stable by construction.
|
|
22
|
+
ce.evolver.genesis.nodes = [
|
|
23
|
+
# ── the committee ──────────────────────────────────────────────────────────────────────────
|
|
24
|
+
{
|
|
25
|
+
node-id = "devnet1-node0"
|
|
26
|
+
host = "devnet.mcsherrylabs.com"
|
|
27
|
+
port = 7000
|
|
28
|
+
public-key = "MCowBQYDK2VwAyEAxTC3mipBz3Ae6nkUzORWLAco21eX3iSSC+QEUSbJMCs="
|
|
29
|
+
bls-public-key = "rCrpHeLH85ECkow6A58I54fjvtCmOHfdm1llPrish+aD49l32sBciyYAzytRxwak"
|
|
30
|
+
quorum-weight = 1000
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
# ── seed nodes: weight 0, so present and trusted but never voting, never leading ───────────
|
|
34
|
+
#
|
|
35
|
+
# ⚠️ Keys are the ones these two nodes ALREADY hold, read from their own /status. Their host
|
|
36
|
+
# secrets are untouched and stay where they are — only the public halves appear here.
|
|
37
|
+
{
|
|
38
|
+
node-id = "evolvermeerkatnode920080"
|
|
39
|
+
host = "65.21.255.224"
|
|
40
|
+
port = 7100
|
|
41
|
+
public-key = "MCowBQYDK2VwAyEAvDNSSDsSYRHfvFCOcl/Fba89VnBrkLzYBy28xqjg8Zg="
|
|
42
|
+
bls-public-key = "iJZMbE+J+TnNnHpYxk2lQDJipTvvUg0qCQNVJViTfoatqZkqazLCZbUmSP4W26Vj"
|
|
43
|
+
quorum-weight = 0
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
node-id = "schednode2"
|
|
47
|
+
host = "65.21.255.224"
|
|
48
|
+
port = 7200
|
|
49
|
+
public-key = "MCowBQYDK2VwAyEAvUOp3fA1nTjv546jme0n14wYDVjHshLL3I7XpQxQ8V4="
|
|
50
|
+
bls-public-key = "ht3U8qRoQtKIU0NUbuuwx/ni4iF+rcAs2dJgxDOcf+YOs+brlSKSnC2XBFBp1rR1"
|
|
51
|
+
quorum-weight = 0
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
# ⚠️ devnet-seed-3 IS NEVER DEPLOYED, ON PURPOSE, AND MUST STAY THAT WAY. A list of seeds is
|
|
55
|
+
# only worth having if a dead entry is walked past rather than waited on, and a seed that is
|
|
56
|
+
# permanently absent tests that on every single join instead of on the one afternoon somebody
|
|
57
|
+
# remembers to stop a live one. Its private key material was generated in a throwaway container
|
|
58
|
+
# and destroyed with it — nobody can bring this node up, which is the point. `.invalid` is
|
|
59
|
+
# reserved by RFC 2606 and can never resolve, so the attempt fails fast instead of hanging.
|
|
60
|
+
# If you are reading this because something reports it unreachable: that is the test passing.
|
|
61
|
+
{
|
|
62
|
+
node-id = "devnet-seed-3"
|
|
63
|
+
host = "devnet-seed-3.invalid"
|
|
64
|
+
port = 7300
|
|
65
|
+
public-key = "MCowBQYDK2VwAyEAHY2onS4+fZss0VZaT84AMcQQugILPagvBm3PFDE5cYY="
|
|
66
|
+
bls-public-key = "oO91EyEt4jvpP3XuuNiT+6F/9djCjAqgs/+e7eI64eGz9iRCcHT5HY17coLLKfsk"
|
|
67
|
+
quorum-weight = 0
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
ce.evolver.consensus {
|
|
72
|
+
# ⚠️ ONE voting node. This is the committee size, not the number of genesis entries — the three
|
|
73
|
+
# seed entries above carry weight 0 and are not counted (NodeBootstrap derives the consensus
|
|
74
|
+
# cluster size with the same `quorumWeight > 0` filter the quorum store and leader rotation
|
|
75
|
+
# already use; before 170 it counted every entry, which would have put the relay threshold
|
|
76
|
+
# beyond anything a one-member committee could reach).
|
|
77
|
+
n-nodes = 1
|
|
78
|
+
block-time-ms = 600
|
|
79
|
+
round-timeout-ms = 1500
|
|
80
|
+
block-reward-tokens = 10000 # generous dev reward: the faucet (5000/identity) is funded from the FIRST blocks, even under quiescence
|
|
81
|
+
snapshot-interval-blocks = 0 # single-node: no snapshot cadence; joiners sync from block 0
|
|
82
|
+
identity-sponsorship-cost = 100
|
|
83
|
+
|
|
84
|
+
# Low idle disk-write: after 3 empty blocks the leader lingers on a 5-MINUTE heartbeat
|
|
85
|
+
# instead of the 600ms cadence (#31: idle-heartbeat-ms > block-time-ms activates quiescence);
|
|
86
|
+
# wake-on-work (node-local, always on) still proposes immediately on incoming txs.
|
|
87
|
+
# CHAIN-WIDE — identical on every node by construction.
|
|
88
|
+
quiescence {
|
|
89
|
+
idle-heartbeat-ms = 300000
|
|
90
|
+
idle-after-empty-rounds = 3
|
|
91
|
+
}
|
|
92
|
+
}
|