-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathdocker-compose.phala.yml
More file actions
203 lines (196 loc) · 9.7 KB
/
Copy pathdocker-compose.phala.yml
File metadata and controls
203 lines (196 loc) · 9.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# Phala CVM deployment: lit-api-server + lit-actions + lit-actions-gvisor + otel-collector
# lit-actions and lit-api-server share a gRPC Unix socket at /tmp/lit_actions.sock;
# lit-actions-gvisor (the any-language runner) serves /tmp/lit_actions_gvisor.sock
# in the same shared volume, and lit-api-server routes /lit_binary_action to it.
# otel-collector runs on the compose bridge network, receives OTLP on 0.0.0.0:4317,
# and forwards telemetry to GCP. lit-actions reaches it via LIT_TELEMETRY_ENDPOINT;
# lit-api-server reads the endpoint from NodeConfig.toml ([observability] telemetry_endpoint).
# Uses tdx.large instance type via phala deploy --instance-type tdx.large
#
# Image placeholders (DOCKER_IMAGE_*) are substituted with @sha256: digests by
# the deploy workflow (DR-1.1, DR-1.2) to ensure verifiable, immutable image
# references for CVM verification. Mutable tags (:latest, :sha) are never used
# in the deployed compose file.
#
# Required secrets (set as encrypted Phala CVM environment variables):
# GCP_SERVICE_ACCOUNT_JSON - GCP service account key (raw JSON or base64-encoded)
# GCP_PROJECT_ID - GCP project ID (e.g. "my-gcp-project")
# STRIPE_SECRET_KEY - Stripe API secret key (sandbox for dev, live for prod)
# STRIPE_PUBLISHABLE_KEY - Stripe API publishable key (sandbox for dev, live for prod)
# CERTBOT_DOMAIN - Custom domain for TLS (e.g. "api.chipotle.litprotocol.com")
# CERTBOT_AWS_ACCESS_KEY_ID - Route 53 IAM access key for DNS-01 challenge
# CERTBOT_AWS_SECRET_ACCESS_KEY - Route 53 IAM secret key for DNS-01 challenge
# CERTBOT_AWS_ROLE_ARN - IAM role ARN for STS assumption (required by dstack-ingress)
# CERTBOT_AWS_REGION - AWS region for STS endpoint (e.g. "us-east-1")
# RUST_LOG filter shared by lit-actions and lit-api-server.
# App code stays at trace; per-module overrides suppress low-value internals:
# h2=info – HTTP/2 frame/stream state machine chatter
# hyper=info – per-request connection lifecycle events
# hyper_util=info – timer and pool bookkeeping
# tonic=info – gRPC codec and transport plumbing
# tower=info – service middleware polling
# reqwest=info – redirect/cookie-jar internals
# rustls=info – TLS handshake and session-ticket noise
# tokio_util=info – codec/framed-read buffer ops
x-rust-log: &rust-log "trace,h2=info,hyper=info,hyper_util=info,tonic=info,tower=info,reqwest=info,rustls=info,tokio_util=info"
services:
lit-actions:
image: ${DOCKER_IMAGE_LIT_ACTIONS}
command:
- lit_actions
- --socket
- /tmp/lit_actions.sock
environment:
RUST_LOG: *rust-log
LIT_TELEMETRY_ENDPOINT: http://otel-collector:4317
volumes:
- lit-socket:/tmp
depends_on:
otel-collector:
condition: service_healthy
required: false
restart: unless-stopped
# Any-language (gVisor) action runner. Serves the identical Action gRPC
# op-loop as lit-actions on its own socket in the shared /tmp volume;
# lit-api-server routes /lit_binary_action here. Needs elevated privilege to
# create nested runsc sandboxes (spikes used privileged — scope down before
# production). NB: adding this service changes compose_hash, a governed
# change in prod.
lit-actions-gvisor:
image: ${DOCKER_IMAGE_LIT_ACTIONS_GVISOR}
command:
- lit_actions_gvisor
- --socket
- /tmp/lit_actions_gvisor.sock
- --rootfs
- /var/lib/lit/sandbox-rootfs
# SECURITY: privileged grants ~full host capabilities. It is the
# spike-validated setting for creating nested runsc sandboxes and is kept
# deliberately until the minimal profile is validated on a Linux TDX CVM
# (tracked follow-up — see gvisor-server/README.md "runsc notes").
# The likely tighter replacement, to confirm before production, is:
# cap_add: [SYS_ADMIN, NET_ADMIN]
# security_opt: [seccomp:unconfined, apparmor:unconfined]
# devices: [] # no /dev/kvm in the CVM (systrap/ptrace only)
# Swap `privileged: true` for that set once verified to run runsc nested.
privileged: true
environment:
RUST_LOG: *rust-log
LIT_TELEMETRY_ENDPOINT: http://otel-collector:4317
volumes:
- lit-socket:/tmp
depends_on:
otel-collector:
condition: service_healthy
required: false
restart: unless-stopped
lit-api-server:
image: ${DOCKER_IMAGE_LIT_API_SERVER}
command: ["lit-api-server"]
working_dir: /app
ports:
- "8000:8000"
environment:
RUST_LOG: *rust-log
LIT_TELEMETRY_ENDPOINT: http://otel-collector:4317
ROCKET_ADDRESS: "0.0.0.0"
ROCKET_PORT: "8000"
# BASE_CHAIN_RPC is populated via an encrypted secret passed in through the Phala CLI/UI
BASE_CHAIN_RPC: ${BASE_CHAIN_RPC}
# Stripe billing keys — resolved by Phala CVM at container startup from encrypted env vars.
# Use sandbox keys for dev/next, live keys for prod.
STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY}
STRIPE_PUBLISHABLE_KEY: ${STRIPE_PUBLISHABLE_KEY}
# Shared bearer secret between lit-payments and lit-api-server for the
# internal cache-invalidation endpoint (/internal/invalidate_balance_cache).
# Must match the value set on lit-payments (Railway env var of the same name).
LIT_INTERNAL_SHARED_SECRET: ${LIT_INTERNAL_SHARED_SECRET}
# gVisor any-language runner gate (CPL-359, CPL-361). Rendered per-deploy:
# testing/manual deploys substitute "true"; the production workflow
# substitutes "false" (see deploy-staging.yml / deploy-prod-1-propose.yml /
# justfile.deploy). Even when "true", /lit_binary_action stays gated until
# the on-chain GVISOR_RUNNER_ENABLED node-config value is also truthy — so
# this is the process-level half of the run-time gate, the contract the
# other. An unsubstituted/empty value fails closed (503 "feature disabled").
LIT_GVISOR_ENABLED: "${LIT_GVISOR_ENABLED}"
volumes:
- lit-socket:/tmp
- /var/run/dstack.sock:/var/run/dstack.sock
# Optional overide for NodeConfig.toml (chain, contract_address).
# - ./NodeConfig.toml:/app/NodeConfig.toml:ro
depends_on:
lit-actions:
condition: service_started
lit-actions-gvisor:
condition: service_started
otel-collector:
condition: service_healthy
required: false
restart: unless-stopped
# OTel Collector — receives OTLP (gRPC/HTTP) on 0.0.0.0:4317/4318 from all
# services on the compose bridge, then exports to GCP Cloud Monitoring,
# Cloud Trace, and Cloud Logging.
#
# NOTE: both lit-api-server and lit-actions must be built with --features otlp
# for OTLP emission. Without that feature they fall back to stdout logging.
otel-collector:
image: ${DOCKER_IMAGE_OTEL_COLLECTOR}
environment:
# GCP_SERVICE_ACCOUNT_JSON and GCP_PROJECT_ID must be set as encrypted
# Phala CVM secrets in the deployment manifest.
GCP_SERVICE_ACCOUNT_JSON: ${GCP_SERVICE_ACCOUNT_JSON}
GCP_PROJECT_ID: ${GCP_PROJECT_ID}
# Host pid namespace + /proc and /sys mounts for the hostmetrics receiver
# to report accurate CVM-level CPU, memory, disk, and network metrics.
# We avoid mounting the entire root filesystem to limit the read surface.
pid: host
volumes:
- '/proc:/host/proc:ro'
- '/sys:/host/sys:ro'
restart: unless-stopped
# dstack-ingress — attestation-bound TLS termination inside the TEE.
# Obtains a Let's Encrypt cert for DOMAIN via DNS-01 (Route 53), then
# reverse-proxies HTTPS :443 → lit-api-server :8000.
dstack-ingress:
image: dstacktee/dstack-ingress:1.4@sha256:11c0481ca1e2ef9c959187ff3c01c7f59c26d631c7717a571ad994b96203bb0b
ports:
- "443:443"
environment:
# dstack-ingress expects DOMAIN, AWS_ACCESS_KEY_ID, and AWS_SECRET_ACCESS_KEY internally.
# Route 53 provider docs (env vars, optional role assumption):
# https://github.com/Dstack-TEE/dstack-examples/blob/main/custom-domain/dstack-ingress/DNS_PROVIDERS.md
DOMAIN: "${CERTBOT_DOMAIN}"
# DSTACK_GATEWAY_DOMAIN is auto-populated by Phala Cloud into the CVM.
# https://docs.phala.com/phala-cloud/networking/specifications
GATEWAY_DOMAIN: "_.${DSTACK_GATEWAY_DOMAIN}"
DNS_PROVIDER: "route53"
TARGET_ENDPOINT: "http://lit-api-server:8000"
CERTBOT_EMAIL: "admin@litprotocol.com"
SET_CAA: "true"
AWS_ACCESS_KEY_ID: "${CERTBOT_AWS_ACCESS_KEY_ID}"
AWS_SECRET_ACCESS_KEY: "${CERTBOT_AWS_SECRET_ACCESS_KEY}"
# STS role assumption — required by dstack-ingress for Route 53 access.
# The IAM user (AWS_ACCESS_KEY_ID) assumes this role to modify DNS records.
AWS_ROLE_ARN: "${CERTBOT_AWS_ROLE_ARN}"
AWS_REGION: "${CERTBOT_AWS_REGION}"
# Raise the nginx proxy timeouts from their 60s default. dstack-ingress
# runs nginx internally for TLS termination; with these unset, a request
# whose response takes >60s gets a 504 from the ingress even though the
# action runtime (DEFAULT_TIMEOUT_MS) allows up to 15 minutes. We match
# that 15-minute runtime budget so the ingress is never the limiting
# factor for a long-running Lit Action (e.g. WASM zk-proving). Note:
# dstack-gateway's upstream TCP proxy still closes connections idle for
# >10 minutes, so a response that needs longer than that with no bytes
# flowing requires streaming/async rather than just a longer timeout.
PROXY_READ_TIMEOUT: "900s"
PROXY_SEND_TIMEOUT: "900s"
volumes:
- /var/run/dstack.sock:/var/run/dstack.sock
- cert-data:/etc/letsencrypt
depends_on:
lit-api-server:
condition: service_started
restart: unless-stopped
volumes:
lit-socket:
cert-data: