Renames the product to Meridian across 1826 files: 24 crates (codebase-chat-* -> meridian-*), the Flutter package, env vars (CODEBASE_CHAT_* -> MERIDIAN_*), the deep-link scheme (meridian://), Postgres GUCs, Helm charts, skills, and the agent surface. White-labels every external identity onto self-hosted infrastructure: hosts move from *.codebase.design to *.meridian.r2d2.office.ilab.zone, images to registry.r2d2.office.ilab.zone/meridian-*, the repo slug to r2d2/meridian, and bundle IDs to zone.ilab.office.r2d2.meridian.*. The Block staging relay and the four Block-internal build repos are not reachable from a self-hosted deployment and are no longer referenced. The mark becomes a pixel M. It is 5x6 rather than a square 5x5 because the avatar-pile mask asserts the hole clears the glyph's right edge: at 5x5 that edge moves from 68.4% to 73% of the tile, which overruns the 56px team-card hole outright and leaves the other three piles under a pixel. At 5x6 the aspect is 0.833 against the retired C's 0.800, so all four masks clear it unchanged. All 59 materializations are regenerated from the generators; `just check-brand` passes. Four things are deliberately NOT renamed, because they match what was *stored* rather than what now ships. Rewriting any of them makes a migration no-op on exactly the installs it exists to repair: - Frozen migrations 0001-0030. Their SHA-256 digests are pinned in n-minus-one-pins.json and embedded in the attested N-1 image. The new vocabulary lands as forward migration 0031, which dual-reads all three generations' GUCs, lock names, app profiles and mesh d_tags. The push-gateway's own 0001 is likewise restored byte-identical, with 0002 widening its app_profile CHECK. - Legacy namespace chains. xyz.block.codebasechat.app is *prepended* to LEGACY_RELEASE_IDENTIFIERS and its dev/localStorage twins, per the rule in legacy_dirs.rs that a previous rename already broke once. - Bead IDs (codebaseChat-*), which are cited from commits and docs. - CHANGELOG history and upstream issue links. The Codebase-era persona ids are added to RETIRED_PERSONAS with their prompts verbatim, but deliberately NOT to RETIRED_PERSONA_REPLACEMENTS: that map drives migration::retire_agents, which deletes deployed instances, and its safety argument is that the successor is already deployed alongside. That held for Buzz->Codebase; nothing provisions a Meridian agent on an install that already onboarded, so mapping these would delete a working agent and leave nothing in its place. The brand-guard self-test changes axis: the M is symmetric about its vertical axis, so a mirrored M *is* the canonical M and asserting a rejection there would assert a bug. It now flips top-to-bottom (the mark reads as a W) and pins the horizontal symmetry so the coupling is visible if the mark ever becomes asymmetric again. Verified: cargo check --workspace --all-targets clean, just fix-all clean, flutter analyze clean, just check-skills pass, check-brand 59/59, brand-core 9/9, avatarPileMask 4/4, starter-avatar contrast 2/2. Signed-off-by: Joshua Belke <joshua@innovationhub-act.org>
6.5 KiB
Meridian Docker Compose deployment
This is the single-node/VPS deployment bundle. It is intentionally separate from
the root docker-compose.yml, which remains local development infrastructure.
Quick start
cd deploy/compose
cp .env.example .env
$EDITOR .env # replace every CHANGE_ME value
./run.sh start
For a public VPS with automatic Let's Encrypt certificates:
cd deploy/compose
MERIDIAN_COMPOSE_TLS=true ./run.sh start
The bootstrap script should eventually replace manual .env editing for normal
users. It is responsible for generating stable secrets and, optionally, an owner
keypair.
Control plane (hosted communities)
compose.control-plane.yml adds the service that mints communities and backs
the desktop app's "Create a new community" flow. It is optional — the relay runs
without it; only provisioning needs it.
docker compose -f compose.yml -f compose.control-plane.yml up -d
Three settings are load-bearing, and each fails in a way that does not look like a configuration error:
- It gets its own database. The relay and the control plane share one
.env, and a bareDATABASE_URLnames the relay's database. Migrating into it interleaves two_sqlx_migrationshistories and corrupts both, socontrol-plane-db-initcreatesmeridian_control_planeseparately. MERIDIAN_CONTROL_RELAY_OPERATOR_API_ORIGINmust equal the relay'sRELAY_OPERATOR_API_ORIGINbyte-for-byte. The relay rebuilds the NIP-98utag as{origin}{path}{?query}and compares it to the signed value, so a trailing slash — orlocalhoston one side and127.0.0.1on the other — fails every operator call with a signature error.- The relay must trust the operator key. Derive it with
meridian-control-plane --print-operator-pubkeyand add the result to the relay'sRELAY_OPERATOR_PUBKEYS, or provisioning is rejected outright.
MERIDIAN_CONTROL_IDENTITY_PROVIDER=dev accepts any email address with no
verification. It refuses to start unless MERIDIAN_CONTROL_ALLOW_DEV_LOGIN=1
is set as well — keep both off anything reachable by someone other than you.
Dokploy
compose.dokploy.yml is an alternative to the Helm/ArgoCD path in ../charts,
for running the stack on a single server under Dokploy.
docker compose -f compose.yml -f compose.control-plane.yml -f compose.dokploy.yml up -d
Dokploy supplies the reverse proxy (Traefik) and certificates, so this overlay is
the mirror image of compose.caddy.yml: it unpublishes host ports and routes
by Traefik label instead. Do not add a ports: entry back — that bypasses the
proxy and exposes the relay directly on the host.
| Host | Service |
|---|---|
register.meridian.r2d2.office.ilab.zone |
control plane (:8090) |
relay.meridian.r2d2.office.ilab.zone |
relay (:3000) |
*.communities.meridian.r2d2.office.ilab.zone |
relay — community derived from the Host header |
Two constraints worth knowing before the first deploy:
dokploy-networkis external, created by Dokploy itself. Bringing this stack up outside Dokploy fails on the missing network. That is the intended signal, not something to work around by declaring the network locally.- The certificate resolver must use DNS-01. Community hosts are minted as
<name>.communities.meridian.r2d2.office.ilab.zone, which needs a wildcard certificate, and Let's Encrypt only issues wildcards over DNS-01. With HTTP-01 the deploy succeeds and then every new community fails to get a certificate — a failure that surfaces per community rather than at deploy time.meridian.r2d2.office.ilab.zoneis already on Cloudflare, so a scoped Cloudflare API token is the least-friction provider.
DNS: point register, relay, and a *.communities wildcard at the server.
Production notes
- Requires Docker Compose v2.24.4 or newer; the TLS override uses Compose's
!resettag to remove the direct relay port when Caddy terminates HTTPS. - Default
MERIDIAN_IMAGEtracksregistry.r2d2.office.ilab.zone/meridian:mainfor early testing. Pin it toregistry.r2d2.office.ilab.zone/meridian:sha-<7>or a semver release tag for production once available. - Keep
MERIDIAN_RELAY_PRIVATE_KEY,MERIDIAN_GIT_HOOK_HMAC_SECRET, database/Redis, and S3 secrets stable across restarts. RELAY_OWNER_PUBKEYis intentionally not prefixed withMERIDIAN_; it must be a 64-character hex Nostr pubkey when closed relay mode is enabled.MERIDIAN_AUTO_MIGRATEis opt-in. SetMERIDIAN_AUTO_MIGRATE=trueor runmeridian-admin migratebefore starting the relay when bootstrapping a fresh database. Auto-migration requires an image that includes embedded SQLx migrations.- The stack uses Postgres, Redis, MinIO, and a git data volume because those are real Meridian dependencies today. Minimal mode can simplify this later.
- The bundled Compose stack fixes the relay endpoint to
http://minio:9000andMERIDIAN_S3_ADDRESSING_STYLE=path: Docker DNS resolvesminio, not<bucket>.minio. It is not configurable for an external S3 provider through.env; use the Helm chart or a custom Compose configuration for providers such as new Railway Storage Buckets that requirevirtualaddressing.
Run ./run.sh backup-hint for the checklist. ./run.sh backup OUTPUT_DIR
pauses relay writes while it captures Postgres and MinIO into one consistency
set; it always resumes the relay on failure. Set BACKUP_SIGNING_KEY to an
external PEM private key and write directly to encrypted storage. The worker
can read and exfiltrate that PEM; read-only mounting is not HSM custody. Keep the
matching public key and four authenticated probes outside the artifact, then
set BACKUP_VERIFY_KEY and RESTORE_PROBES_DIR before running
./run.sh restore-verify OUTPUT_DIR. The verifier restores the matching relay
and exact recorded Redis-protocol images with auto-migration disabled into
isolated containers. It proves identity, audit chains, event counts,
empty-state rebuild/readiness, exhaustive database-to-Git/media object graph
integrity, ACL denial, Git refs, media hashes, and workflow-secret decryption,
and fails the configured artifact-age and technical restore-time gates.
See docs/runbooks/relay-backup-restore.md.
Validation
Before sharing an install link publicly, verify a fresh install with:
cd deploy/compose
cp .env.example .env
$EDITOR .env
./run.sh config
./run.sh start
curl -fsS "http://127.0.0.1:$(grep -E '^MERIDIAN_HTTP_PORT=' .env | cut -d= -f2-)/_liveness"
./run.sh status