Deploy¶
Phase 1 + Phase 2-2a deployment on HK-133 (lab.flytoex.net,
45.145.229.197, ssh port 43475).
Topology¶
Internet ──:443/:80──> Caddy ──> /admin/proxy/* ── basic_auth ──> relay:7080 (web, bridge-internal)
├ /admin/* ─────────────────> common:8081 (admin HTTP surface)
├ /api/v1/* ─────────────────> common:8080 (business REST/SSE -- ADR-0002)
└ everything else ──────────────── > logistics:8080 (REST)
└─> common:9090 (gRPC, compose bridge)
Internet ──:7000/:7001/:13000-13099──> relay (bridge) ── tunnel ── flyto-proxy (alibaba VPC, 2-2b, not yet deployed)
└─> internal DB / HTTP / SSH
hub.flytoex.netresolves to HK-133 (grey cloud, DNS-only)- Caddy auto-provisions Let's Encrypt certs on first request
- All services on the compose-default bridge network
commongRPC:9090and admin HTTP:8081both not exposed to the host -- only reachable through Caddy or fromlogisticsover the compose bridgerelayweb:7080is not in composeports:, so Caddy (reverse_proxy relay:7080) is the only external ingress. Tunnel ports:7000/:7001plus the rule-listen range:13000-13099ARE host-exposed, because alibaba-side proxies connect to:7000and external callers hit the per-rule listen ports that relay binds at runtime.ForwardRule.listen_portvalues in proxy configs must fall in13000-13099; outside that range needs adocker-compose.ymlupdate and redeploy.
First-time install on HK-133¶
ssh -p 43475 root@45.145.229.197
git clone https://git.flytoex.net/yuanwei/Flyto-Agent.git /opt/flyto
cd /opt/flyto/deploy
# After v0.1.0-alpha tag is pushed and Gitea Actions finishes the build:
docker compose pull
docker compose up -d
docker compose logs -f caddy # watch LE cert issuance
Verify round-trip:
curl https://hub.flytoex.net/health
# => {"status":"STATUS_UNSPECIFIED","version":"","tenantId":""}
# STATUS_UNSPECIFIED is expected in dev mode (no auth, no tenant in ctx).
# Once OIDC is wired, the same call with a valid bearer returns SERVING.
Admin HTTP surface on common (observability only, not business traffic):
curl https://hub.flytoex.net/admin/health # => {"status":"ok"}
curl https://hub.flytoex.net/admin/version # => {"version":"v0.1.0-alpha.X"}
curl https://hub.flytoex.net/admin/tenant # => {"tenant_id":"","subject":""} in dev
# /admin/tenant requires a bearer token once OIDC is wired; in dev it echoes
# empty strings. /admin/health and /admin/version are always open.
Business REST/SSE on common (ADR-0002 -- engine consumption surface):
# Health on the business listener (separate from /admin/health, but same shape)
curl https://hub.flytoex.net/api/v1/health # => {"status":"ok","timestamp":...}
# Streaming agent run (SSE; requires ANTHROPIC_API_KEY in deploy/.env)
curl -N https://hub.flytoex.net/api/v1/agent/run \
-H 'Authorization: Bearer <oidc-jwt>' \
-H 'Content-Type: application/json' \
-d '{"prompt":"hello"}'
# => streaming text/event-stream chunks (text_delta / tool_use / done)
# Caddy passes SSE through with flush_interval -1; expect first chunk in ms.
Relay web console (flyto-proxy):
# Browser: open https://hub.flytoex.net/admin/proxy/ and enter basic_auth
# credentials (see the "Gitea Actions secrets" section below for where the
# hash is stored; plaintext user/password are in flysafe).
# curl with basic_auth:
curl -u yuanwei:<password> https://hub.flytoex.net/admin/proxy/api/proxies
# => [] (empty until an alibaba-side proxy connects -- that is Phase 2-2b)
Gitea Actions secrets¶
Go to https://git.flytoex.net/yuanwei/Flyto-Agent/-/settings/actions/secrets
and add:
| Secret | Value |
|---|---|
REGISTRY_USER |
yuanwei |
REGISTRY_TOKEN |
Personal Access Token with write:package scope |
ADMIN_BASIC_AUTH_HASH |
caddy hash-password output for the /admin/proxy/* basic_auth account. Plaintext credentials live in flysafe. |
FLYTO_PROXY_TOKEN |
Shared token between relay and the alibaba-side flyto-proxy (64-hex, openssl rand -hex 32). Same value must be baked into the alibaba proxy config. |
Generate the PAT at https://git.flytoex.net/-/user/settings/applications.
Note: GITEA_* / GITHUB_* prefixes are reserved by Actions; use
REGISTRY_* / ADMIN_* / FLYTO_*.
Release flow¶
Tag push triggers .gitea/workflows/release.yml:
- Build
git.flytoex.net/yuanwei/flyto-agent-common:<version> - Build
git.flytoex.net/yuanwei/flyto-agent-logistics:<version> - Push both plus
:latestto Gitea registry
Tag push now triggers auto-deploy via the deploy workflow job (SSH to
HK-133, docker compose pull + up -d). Manual roll (skipping the deploy
job) is only needed for troubleshooting:
Deploy secrets in the Gitea repo: DEPLOY_HOST, DEPLOY_PORT,
DEPLOY_USER, DEPLOY_SSH_KEY (ed25519 key pair; pub already in
/root/.ssh/authorized_keys on HK-133, keyed by
flyto-deploy@gitea-actions).
Fast-path deploys¶
Three deploy paths exist, ordered by speed:
| Path | Tool | Time | When to use |
|---|---|---|---|
| Full release | git tag v... push |
~3-10 min | Dockerfile / compose / Caddyfile / new deps / non-Go services |
| Image fast-push | bash deploy/fastpush.sh <svc> |
~1-2 min | Single-image change without retagging (any of 5 services) |
| Binary fast-push | bash deploy/fastpush-bin.sh <svc> |
~50 s | Pure Go change to common or bill-recon-web |
Binary bind-mount (alpha.20+)¶
common and bill-recon-web ship as "thin shell image + bind-mount
binary": the image still embeds a /common (or /bill-recon-web)
binary as a rollback / immutable-artifact safety net, but compose
overlays it at runtime with whatever sits at
/opt/flyto/bin/<svc>/current/<svc> on hub. fastpush-bin.sh
cross-compiles the Go binary locally, scps it to a versioned dir on
hub, atomically swaps the current symlink, and docker compose
restarts the service. ~50 s end-to-end vs ~3 min for a full image
build. See deploy/fastpush-bin.sh header for the full design.
release.yml's deploy job has a pre-up guard that fails loudly if
/opt/flyto/bin/<svc>/current/<svc> is missing (file bind-mount
requires the source path to exist before compose up). Bootstrap on
a fresh hub is bash deploy/fastpush-bin.sh <svc> from a build host
once.
Rollback (post-bind-mount)¶
Rolling git checkout back to an older tag does not roll back the binary -- the binary lives on host outside the image. To roll back:
ssh hub 'cd /opt/flyto/bin/<svc> && ls -lt v*' # pick old version
ssh hub 'cd /opt/flyto/bin/<svc> && ln -sfn v<old-sha> current'
ssh hub 'cd /opt/flyto/deploy && docker compose restart <svc>'
fastpush-bin.sh keeps the last 5 versions by mtime in
/opt/flyto/bin/<svc>/v*/. Older releases prune automatically.
Production mode (OIDC)¶
Once an OIDC issuer is available, override the common service command:
services:
common:
command:
- "--grpc-addr=:9090"
- "--http-addr=:8081"
- "--rest-addr=:8080"
- "--oidc-issuer=https://auth.flytoex.net/realms/flyto"
- "--oidc-audience=flyto-platform"
- "--oidc-tenant-claim=tenant_id"
Rolling back to dev mode = remove the last three --oidc-* flags. Leaving
--http-addr empty is also legal and disables the admin surface entirely
(e.g. for single-purpose gRPC-only builds). Leaving --rest-addr empty
disables business REST/SSE without affecting gRPC + admin paths -- the
ANTHROPIC_API_KEY ${...:?...} requirement still triggers, so set the env
var to any non-empty placeholder when deploying without business REST.