Deployment¶
Target¶
The pilot runs under the dedicated OVH OS user heyaira with rootless Docker:
- application checkout:
/srv/heyaira/app; - protected token files:
/srv/heyaira/app/secrets; - Compose project:
/srv/heyaira/app/compose.yaml; - PostgreSQL data:
/srv/heyaira/app/postgres-data; - application loopback port:
127.0.0.1:18080; - public MCP endpoint:
https://mcp.heyaira.eu/mcp.
The application container and PostgreSQL container must not publish PostgreSQL to a public host interface. The production .env file and project token file stay outside Git and are readable only by heyaira.
DNS and HTTPS¶
The heyaira.eu zone is now managed by Cloudflare. The active MCP record is:
mcp.heyaira.eu. A 51.83.159.109 (Proxied)
Cloudflare's edge certificate is active and the zone is currently using Full encryption. The host administrator can run the repository's ops/caddy-heyaira.sh helper as root. It creates a recoverable backup, adds only the HeyAira site when absent, validates the full configuration, and reloads Caddy. Its resulting site block is:
mcp.heyaira.eu {
reverse_proxy 127.0.0.1:18080
}
For the current Dockerized Caddy deployment, the active site block uses the
private relay 172.21.0.1:18081 instead. Keep the bootstrap script's direct
loopback example only for hosts where Caddy runs directly on the host; do not
replace an existing relay route without validating the complete Caddy setup.
The script must be run on the OVH host from a checkout of this repository, for example:
cd /srv/heyaira/app
sudo ./ops/caddy-heyaira.sh
The heyaira runtime user intentionally cannot run this command. It must be executed through a separate root-admin channel; do not use Voyana credentials. After Caddy obtains its origin certificate, change Cloudflare SSL/TLS encryption from Full to Full (strict).
Caddy provides the public TLS certificate. Do not expose port 18080 directly; it is the private edge-to-container hop.
Application deployment¶
From the server, as heyaira:
cd /srv/heyaira/app
chmod 600 .env
mkdir -p secrets && chmod 700 secrets
docker compose up -d --build
docker compose ps
curl --fail http://127.0.0.1:18080/healthz
Create a project token once, writing it to a protected path. The command never prints the token:
docker compose exec server heyaira-admin create-project \
--name "HeyAira" \
--repository-url "https://github.com/h8v6/heyaira" \
--token-label "primary" \
--token-file /run/heyaira-secrets/heyaira.token
The token is then entered into the lab's protected MCP client configuration. It must not be put into GitHub, /docs, shell history, or a chat message.
Project access is administered separately from the MCP protocol. Grant a stable identity a project role with:
docker compose exec server heyaira-admin add-membership \
--project-id "<project-id>" \
--subject "github:user:<numeric-id>" \
--role contributor
Bind another GitHub repository to the project without copying its contents:
docker compose exec server heyaira-admin link-repository \
--project-id "<project-id>" \
--github-owner "<owner>" \
--github-repository "<repository>" \
--repository-url "https://github.com/<owner>/<repository>" \
--default-branch main
GitHub App installation and repository API operations remain a separate future integration; these commands only maintain HeyAira's project bindings.
Verification¶
Run the synthetic smoke test from a machine outside the OVH host after DNS and HTTPS are active:
.venv/bin/python scripts/smoke_mcp.py \
--url https://mcp.heyaira.eu/mcp \
--token-file /secure/local/path/heyaira-token \
--profile /secure/local/path/heyaira-public-profile.json
The acceptance check includes unauthenticated rejection, MCP discovery, memory write/read, task state, work receipt persistence, and stale-version rejection.
For the local independent-client continuity check, run:
.venv/bin/python scripts/multi_client_smoke.py \
--url http://127.0.0.1:18080/mcp \
--token-file secrets/heyaira.token \
--profile /secure/local/path/heyaira-local-test-profile.json
The test uses two separate MCP client sessions and synthetic data only. It does not share a transcript or a scratch file between the simulated sessions.
Binding activation and rollback¶
The compatible new pair is the server at the BIND-FIX result commit and the
client scripts from that same commit, using an operator-selected profile whose
resource, assigned instance_id, and project UUID are pinned. Activation is
ordered: apply migrations 006 and 007, set explicit HEYAIRA_INSTANCE_ID,
HEYAIRA_RESOURCE (or leave it empty so it derives from the explicit public
URL), and HEYAIRA_ENVIRONMENT, deploy the server, refresh client tool
schemas, verify server_identity against the profile for programmatic clients,
run the synthetic smoke suite, reconnect the web clients through OAuth so their
credentials receive the instance binding, and only then enable writes.
The compatible rollback pair is the pre-binding server/client revision
45dd81e6f7effeb28c019cee833d90346ca6cbf7 with its legacy static-token
profile. Rollback requires redeploying that server revision together with the
matching pre-binding clients; selecting an old client profile while leaving
the new server live only produces the documented refusal. Migration 006 is
additive: do not drop its tables, delete audit history, or rewrite existing
domain rows during rollback. Returning to a server without the binding guard
removes the protection, so it is an operational compatibility rollback rather
than a security-equivalent state.