Skip to content

Operations: Hetzner + Cloudflare

Status: staging-operated, fixture only. Live staging on 46.225.87.105 (gestalt-evo) bound to 127.0.0.1:3011 with SurrealDB-remote storage. No public Cloudflare DNS/TLS/access mutation has been performed. Production admission is disabled. Customer data is forbidden. Authoritative source: the deploy/hetzner-cloudflare/README.md log.

The Hetzner host runs sibling Gestalt services from the upstream project — gestalt, gestalt-server, gestalt-images — out of /opt/gestalt, /opt/gestalt-evo, and /opt/gestalt-evo-src. The Aion fixture deploy must never touch any of those.

The Aion deploy is scoped strictly to:

service: gestalt-fixture
path: /opt/gestalt-fixture
bind: 127.0.0.1:3011

Any operation that reaches outside that scope is a violation. Do not invoke the sibling repo’s deploy scripts. Do not systemctl the sibling services. Do not rm outside /opt/gestalt-fixture.

This rule exists because the sibling project owns the host’s user- facing presence. A wipe of /opt/gestalt would destroy a live system the Aion runtime has no business managing.

Per deploy/hetzner-cloudflare/README.md:

host: 46.225.87.105 (gestalt-evo)
service: gestalt-fixture (active, enabled)
binary: gestalt-cloud --fixture --no-root-key --storage-profile surreal-remote
bind: 127.0.0.1:3011
remote dir: /opt/gestalt-fixture
storage: SurrealDB remote profile at 127.0.0.1:8000 (gestalt_aion_cloud / staging)
runtime snapshot: cloud_runtime_snapshot:fixture_hosted_operator
durable commits: cloud_atom_commit (signed atom rows with signer/storage provenance)
root signing: disabled; hosted operator rehydrates from durable grant atom
remote smoke: /health /ready /version /metrics /v1/membrane/contract → 200
/v1/auth/session/exchange, /v1/tenant/self,
/v1/authority/resolve-context, /v1/shop/prepare,
/v1/shop/commit, /v1/commits/recent,
/v1/receipts/verify, /v1/proofs/request → 200
signed commit response includes hosted-operator signer provenance
and surreal-remote storage profile before and after service restart;
recent commit journal keeps the pre-restart atom after restart
/v1/raw-db → 404
non-wipe smoke: GESTALT_SKIP_WIPE=1 passed against the running remote state
public edge: no Cloudflare DNS/TLS/access mutation performed
production admission: disabled
customer data: forbidden

The Hetzner host is Linux x86_64; local dev is macOS. The deploy defaults to target/release/gestalt, which on macOS produces a Mach-O binary that fails on the server with Exec format error. Cross-compile a static musl binary:

Terminal window
# one-time
rustup target add x86_64-unknown-linux-musl
brew install zig
cargo install cargo-zigbuild
# build
cargo zigbuild --release --target x86_64-unknown-linux-musl -p gestalt-cloud

Then deploy with GESTALT_BINARY pointing at the musl binary:

Terminal window
GESTALT_LIVE_HETZNER_COMMIT=YES_DEPLOY_GESTALT_AION_FIXTURE_TO_HETZNER \
GESTALT_DRY_RUN=0 \
GESTALT_STORAGE_PROFILE=surreal-remote \
GESTALT_NO_ROOT_KEY=1 \
GESTALT_SURREAL_REMOTE_ENDPOINT=ws://127.0.0.1:8000 \
GESTALT_SURREAL_REMOTE_NAMESPACE=gestalt_aion_cloud \
GESTALT_SURREAL_REMOTE_DATABASE=staging \
GESTALT_SURREAL_REMOTE_TLS_REQUIRED=false \
GESTALT_SURREAL_REMOTE_USERNAME=<root-user> \
GESTALT_SURREAL_REMOTE_PASSWORD=<root-password> \
GESTALT_BINARY="$PWD/target/x86_64-unknown-linux-musl/release/gestalt-cloud" \
./deploy/hetzner-cloudflare/commit-hetzner-fixture.sh

GESTALT_NO_ROOT_KEY=1 is correct only when the staged world has a durable hosted-operator grant. Otherwise signed fixture commits use the explicit in-process root signer boundary.

The default commit-hetzner-fixture.sh invocation above wipes the remote /opt/gestalt-fixture directory before deploying. Use this when you’ve shipped new binary or configuration.

Terminal window
GESTALT_SKIP_WIPE=1 ./deploy/hetzner-cloudflare/commit-hetzner-fixture.sh

This path checks the live service, commits through the hosted delegate, verifies /v1/commits/recent, restarts the service, and verifies the pre-restart atom is still present. Use this to prove durability without disturbing state.

Terminal window
GESTALT_DEPLOY_TARGET=staging GESTALT_HOST=staging.example.invalid \
GESTALT_ALLOW_WIPE=YES_WIPE_DISPOSABLE_STAGING \
./deploy/hetzner-cloudflare/wipe-staging.sh

Defaults are dry-run. Real destructive staging wipe requires:

GESTALT_DEPLOY_TARGET=staging
GESTALT_HOST=<host>
GESTALT_ALLOW_WIPE=YES_WIPE_DISPOSABLE_STAGING
GESTALT_DRY_RUN=0

All M-trial scripts can target the remote staging through an SSH localhost tunnel. The standard pattern:

Terminal window
# in one terminal, open the tunnel
ssh -i ~/.ssh/gestalt_deploy -L 13011:127.0.0.1:3011 root@46.225.87.105
# in another terminal, run trials against http://127.0.0.1:13011
GESTALT_BASE_URL=http://127.0.0.1:13011 \
GESTALT_TRIAL_OUT_DIR=target/gestalt-m12-trial-remote \
deploy/hetzner-cloudflare/m12-worldline-runtime-trial.sh

The full set of remote-tested M-trials is recorded in deploy/hetzner-cloudflare/README.md: M6, M7, M8, M9, M10/M11, M12, M13, M14.

There is no Cloudflare DNS/TLS/access mutation today. The fixture service binds to 127.0.0.1:3011 only. Public edge access is gated; see workflow 018 finding 5.

When a public edge lands, it will:

  • be a confirmed staging hostname (e.g. staging.gestalt.example),
  • sit behind explicit Cloudflare Access or Caddy reverse proxy,
  • block raw DB paths (/v1/raw-db → 404 already),
  • log remote smoke evidence with date and hostname.
  • No customer data. Real customer information must not enter the fixture deploy.
  • No production legal claims. Receipts emitted by the staging fixture must not be presented as real proof to any third party.
  • Don’t restart unless safe. Some trials assume the in-process hosted operator state has been seeded by a prior trial. Restart patterns are documented per trial.
  • Don’t enable runtime overlay enforcement on remote. M13 lifecycle mutation routes (session revoke, key rotate) must run with enforce_runtime_overlay: false on remote staging so the no-wipe smoke continues to work afterward.
  • Local fixture
  • Observability
  • deploy/hetzner-cloudflare/README.md — operator log
  • Memory note: Hetzner co-tenancy with upstream gestalt services (referenced in MEMORY.md at the project root for AI sessions)