Skip to content

Commit 743e98e

Browse files
committed
Merge remote-tracking branch 'origin/main' into glitch003/lambda-parity-payments-storage
# Conflicts: # lit-api-server/src/accounts/signable_contract.rs # lit-api-server/src/main.rs # lit-payments/src/config.rs # lit-payments/src/main.rs
2 parents f3fcc50 + f445e1a commit 743e98e

136 files changed

Lines changed: 27322 additions & 2308 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/contract-bindings-check.yml

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ jobs:
1919
github.event.pull_request.head.repo.full_name == github.repository
2020
name: contract-bindings
2121
runs-on: self-hosted
22-
concurrency:
23-
group: foundry-toolchain
24-
cancel-in-progress: false
2522
steps:
2623
- uses: actions/checkout@v4
2724

@@ -37,8 +34,17 @@ jobs:
3734
- name: Check Foundry toolchain
3835
id: foundry-toolchain
3936
run: |
40-
if command -v forge >/dev/null 2>&1 && forge --version | grep -Eq '(^|[^0-9])1\.5\.1([^0-9]|$)'; then
41-
forge --version
37+
# The global install on the shared runner lives in ~/.foundry/bin, which
38+
# is only on PATH via the user's shell profile. GH Actions steps run in a
39+
# non-login shell that doesn't source it, so probe the known location too.
40+
forge_bin="$(command -v forge || true)"
41+
if [ -z "$forge_bin" ] && [ -x "$HOME/.foundry/bin/forge" ]; then
42+
forge_bin="$HOME/.foundry/bin/forge"
43+
fi
44+
if [ -n "$forge_bin" ] && "$forge_bin" --version | grep -Eq '(^|[^0-9])1\.5\.1([^0-9]|$)'; then
45+
"$forge_bin" --version
46+
# Put it on PATH for later steps so they don't trigger a reinstall.
47+
echo "$(dirname "$forge_bin")" >> "$GITHUB_PATH"
4248
echo "installed=true" >> "$GITHUB_OUTPUT"
4349
else
4450
echo "installed=false" >> "$GITHUB_OUTPUT"

.github/workflows/contract-tests.yml

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ jobs:
1919
github.event.pull_request.head.repo.full_name == github.repository
2020
name: forge-test
2121
runs-on: self-hosted
22-
concurrency:
23-
group: foundry-toolchain
24-
cancel-in-progress: false
2522
steps:
2623
- uses: actions/checkout@v4
2724
with:
@@ -41,8 +38,17 @@ jobs:
4138
- name: Check Foundry toolchain
4239
id: foundry-toolchain
4340
run: |
44-
if command -v forge >/dev/null 2>&1 && forge --version | grep -Eq '(^|[^0-9])1\.5\.1([^0-9]|$)'; then
45-
forge --version
41+
# The global install on the shared runner lives in ~/.foundry/bin, which
42+
# is only on PATH via the user's shell profile. GH Actions steps run in a
43+
# non-login shell that doesn't source it, so probe the known location too.
44+
forge_bin="$(command -v forge || true)"
45+
if [ -z "$forge_bin" ] && [ -x "$HOME/.foundry/bin/forge" ]; then
46+
forge_bin="$HOME/.foundry/bin/forge"
47+
fi
48+
if [ -n "$forge_bin" ] && "$forge_bin" --version | grep -Eq '(^|[^0-9])1\.5\.1([^0-9]|$)'; then
49+
"$forge_bin" --version
50+
# Put it on PATH for later steps so they don't trigger a reinstall.
51+
echo "$(dirname "$forge_bin")" >> "$GITHUB_PATH"
4652
echo "installed=true" >> "$GITHUB_OUTPUT"
4753
else
4854
echo "installed=false" >> "$GITHUB_OUTPUT"

.github/workflows/deploy-staging.yml

Lines changed: 259 additions & 31 deletions
Large diffs are not rendered by default.

.github/workflows/wait-for-api-restart.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ on:
2626
api_root_url:
2727
description: "API base URL (e.g. https://example.com/core/v1)"
2828
value: ${{ jobs.wait.outputs.api_root_url }}
29+
base_url:
30+
description: "Site base URL without the /core/v1 suffix (e.g. https://example.com)"
31+
value: ${{ jobs.wait.outputs.base_url }}
2932
inputs:
3033
expected_sha:
3134
description: "Expected git commit SHA for the new deployment (omit to skip version check)"
@@ -60,6 +63,7 @@ jobs:
6063
runs-on: ${{ fromJson(inputs.runner) }}
6164
outputs:
6265
api_root_url: ${{ steps.resolve-url.outputs.api_root_url }}
66+
base_url: ${{ steps.resolve-url.outputs.base_url }}
6367
steps:
6468
- name: Checkout (for git describe)
6569
if: inputs.expected_sha != ''
@@ -106,6 +110,7 @@ jobs:
106110
exit 1
107111
fi
108112
echo "api_root_url=$BASE_URL" >> "$GITHUB_OUTPUT"
113+
echo "base_url=${BASE_URL%/core/v1}" >> "$GITHUB_OUTPUT"
109114
echo "API base URL: $BASE_URL"
110115
111116
- name: Wait for expected version

Dockerfile.lit-api-server

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ FROM debian:bookworm-slim AS runtime
1919
RUN apt-get update && apt-get install -y \
2020
ca-certificates \
2121
libssl3 \
22+
curl \
2223
&& rm -rf /var/lib/apt/lists/*
2324

2425
WORKDIR /app

SECURITY.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Security Policy
2+
3+
Lit Chipotle runs inside Intel TDX Trusted Execution Environments with key release
4+
gated by smart contracts on Base. We take the security of the stack — code,
5+
contracts, deployment pipeline, and governance — seriously, and we welcome reports
6+
from the security community.
7+
8+
## Reporting a vulnerability
9+
10+
**Please do not open a public GitHub issue for security vulnerabilities.**
11+
12+
Report privately, via either:
13+
14+
- **Email:** [security@litprotocol.com](mailto:security@litprotocol.com) (preferred).
15+
Encrypt sensitive details with our PGP key if you have one; ask in your first
16+
message and we will provide it.
17+
- **GitHub:** open a [private security advisory](https://github.com/LIT-Protocol/chipotle/security/advisories/new)
18+
on this repository.
19+
20+
In your report, please include:
21+
22+
- A description of the issue and its potential impact.
23+
- Steps to reproduce, or a proof of concept.
24+
- The component affected (API server, Lit Actions runtime, contracts, deployment/CI,
25+
governance, attestation/verification flow).
26+
- Any relevant version, commit hash, compose hash, or deployment URL.
27+
28+
## What to expect
29+
30+
- **Acknowledgement** within 3 business days.
31+
- **Triage and severity assessment** within 7 business days, with an initial
32+
remediation plan.
33+
- **Coordinated disclosure.** We will work with you on a disclosure timeline and
34+
credit you (if you wish) once a fix is released. Please give us a reasonable window
35+
to remediate before any public disclosure.
36+
37+
## Scope
38+
39+
In scope:
40+
41+
- The `lit-api-server`, `lit-actions` runtime, and `lit-static` dashboard.
42+
- Smart contracts in this repository (account/permission model, and the
43+
attestation-governance contracts on Base).
44+
- The deployment pipeline and TEE attestation / verification flow.
45+
- The on-chain governance and key-release model.
46+
47+
Out of scope (report upstream, but tell us if it affects us):
48+
49+
- Vulnerabilities in Intel TDX, the dstack OS, or Phala Cloud infrastructure —
50+
report to [Intel](https://www.intel.com/content/www/us/en/security-center/default.html),
51+
[dstack](https://github.com/Dstack-TEE/dstack), or
52+
[Phala](https://docs.phala.com/) respectively.
53+
- Third-party dependencies — report upstream; we track advisories via `deny.toml`.
54+
55+
## Verifying the production deployment
56+
57+
Much of our security model is publicly verifiable rather than asserted. Before
58+
reporting an issue that depends on "what code is running," confirm the live state
59+
yourself:
60+
61+
- [What Is Attestation?](docs/architecture/verification/attestation.mdx) — the model in plain English.
62+
- [Upgrade Governance](docs/architecture/verification/upgrade-governance.mdx) — how releases are approved (2-of-4 Safe on Base).
63+
- [On-Chain KMS](docs/architecture/verification/onchain-kms.mdx) — how key release is gated.
64+
- [Verify in 30 Seconds](docs/architecture/verification/quick-verify.mdx) — Trust Center report + three terminal commands.
65+
66+
## Operational security references (internal)
67+
68+
- [Upgrade governance & release approval](architectureDocs/deployment/vm-code-upgrade.md)
69+
- [Incident response & key-compromise runbook](architectureDocs/deployment/incident-response.md)
70+
- [Secrets management](architectureDocs/deployment/secrets-management.md)
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Incident Response & Key-Compromise Runbook
2+
3+
How to respond when something in the trust chain goes wrong. The planned-upgrade and
4+
rollback flow lives in [vm-code-upgrade.md](vm-code-upgrade.md); this document covers
5+
the *adversarial* and *failure* scenarios that one does not.
6+
7+
> **Status:** several response steps below depend on operational decisions that are
8+
> not yet finalized (marked **TODO/DECIDE**). Treat this as the working runbook and
9+
> resolve the open items before relying on it in a real incident.
10+
11+
## Roles & contacts
12+
13+
| Role | Who | Responsibility |
14+
|---|---|---|
15+
| Incident commander | **TODO/DECIDE** | Owns the response, declares severity, coordinates comms |
16+
| Safe signers | 4 keyholders (2-of-4 quorum) | Execute on-chain governance actions (whitelist removal, owner change) |
17+
| Phala account owner | **TODO/DECIDE** | Holds `PHALA_CLOUD_API_KEY`; can redeploy/stop CVMs |
18+
| Comms / disclosure | **TODO/DECIDE** | External notifications, status page, `security@` triage |
19+
20+
Confirm the live Safe signer set before any action:
21+
22+
```bash
23+
cast call 0xF688411c0FFc300cAb33EB1dA651DBb3E6891098 \
24+
"getOwners()(address[])" --rpc-url https://mainnet.base.org
25+
```
26+
27+
## Key facts that shape the response
28+
29+
- **Governance is 2-of-4 with no timelock.** A quorum can act immediately — good for
30+
emergency response, but also means two compromised signer keys are sufficient to
31+
change the whitelist. There is no delay to "catch" a malicious change in flight;
32+
detection must be real-time (see [Monitoring](#detective-controls)).
33+
- **Key derivation is deterministic per compose hash.** The same attested code always
34+
derives the same application keys from the KMS root. This is why rollback works
35+
without re-issuance — and why a compromised *code version* that obtained keys must
36+
be treated as having compromised the data those keys protect.
37+
- **Removing a compose hash blocks key issuance, not a running process.** See below.
38+
39+
## Scenario 1 — Malicious or vulnerable compose hash got whitelisted
40+
41+
Goal: stop the bad version from obtaining keys and serving.
42+
43+
1. **Revoke the whitelist.** Have the Safe execute `removeComposeHash(badHash)` on
44+
`DstackApp` (`0x3F91…05FfC`). 2-of-4 approval required.
45+
2. **Stop the running CVM.** Revocation prevents *future* key issuance and prevents
46+
the version from booting again — but a CVM that already holds its keys keeps
47+
running until restarted. The Phala account owner must explicitly **stop/redeploy**
48+
the affected CVM via the Phala CLI/dashboard to take it out of service.
49+
**TODO/DECIDE:** confirm exact Phala command and whether gateway can drain traffic
50+
first.
51+
3. **Re-deploy a known-good version** whose compose hash is still whitelisted.
52+
4. **Assess data exposure.** Any data the bad version could decrypt with its derived
53+
keys must be considered exposed.
54+
55+
## Scenario 2 — Safe signer key compromised
56+
57+
1. **If still below quorum (1 key):** rotate immediately but no funds/governance at
58+
immediate risk. The remaining 3 honest signers still control the 2-of-4.
59+
2. **Replace the signer:** Safe executes `swapOwner` (or `removeOwner` +
60+
`addOwnerWithThreshold`) to drop the compromised key. 2-of-4 of the *honest*
61+
signers required.
62+
3. **If two keys compromised (quorum reached by attacker):** treat as full governance
63+
compromise — the attacker can whitelist arbitrary code. **TODO/DECIDE:** there is
64+
currently no higher-authority backstop above the Safe. Mitigations to evaluate:
65+
raising the threshold, adding a timelock to create a reaction window, or a
66+
guardian/recovery module.
67+
68+
## Scenario 3 — `PHALA_CLOUD_API_KEY` leaked
69+
70+
The Phala API key can redeploy and reconfigure CVMs and read/set CVM secrets. It
71+
**cannot** mint keys for un-whitelisted code (the on-chain gate still holds), but it
72+
can disrupt availability and reconfigure encrypted env injection.
73+
74+
1. **Revoke the key** in the Phala dashboard (Avatar → API Tokens) and issue a new one.
75+
2. **Rotate all CVM-injected secrets** (see Scenario 5) — assume they were readable.
76+
3. **Audit recent deployments** for unexpected CVM changes.
77+
78+
## Scenario 4 — Base / KMS availability failure
79+
80+
- The KMS issues keys at boot. If Base RPC or the KMS is unreachable, **already-running
81+
CVMs are unaffected** (they hold their keys); only restarts/new boots are blocked.
82+
- Do **not** force a redeploy during a KMS outage — a restarted CVM may fail to
83+
obtain keys and go down.
84+
- **TODO/DECIDE:** document RPC redundancy / fallback endpoints used by the KMS path.
85+
86+
## Scenario 5 — Secret compromise (Stripe, AWS, GCP, RPC)
87+
88+
These are injected as encrypted Phala env vars and are **outside the attestation
89+
measurement** (not part of the compose hash). See
90+
[secrets-management.md](secrets-management.md).
91+
92+
1. Rotate the upstream credential (Stripe dashboard, AWS IAM, GCP service account).
93+
2. Update the encrypted Phala secret and redeploy.
94+
3. Scope the blast radius per credential (e.g. Route53 IAM should be DNS-01 only).
95+
96+
## Detective controls
97+
98+
Real-time detection matters more here than usual because there is no governance
99+
timelock to provide a reaction window.
100+
101+
- **Alert on governance writes.** Monitor `DstackApp` and the Safe for
102+
`addComposeHash` / `removeComposeHash`, `addOwner` / `swapOwner` / `removeOwner`,
103+
and `changeThreshold` events; page on any unplanned occurrence. **TODO/DECIDE:**
104+
wire this into the existing Grafana stack (see chipotle monitoring) or a
105+
contract-watcher.
106+
- **Alert on attestation drift.** Periodically confirm the live `compose_hash` from
107+
`/info` is still whitelisted and matches the expected release.
108+
- **Alert on CVM lifecycle changes** from the Phala account.
109+
110+
## Post-incident
111+
112+
- Capture a timeline and the on-chain transaction hashes of every governance action.
113+
- File a coordinated-disclosure note per [SECURITY.md](../../SECURITY.md) if external
114+
parties are affected.
115+
- Open follow-ups for any **TODO/DECIDE** item this incident exercised.
116+
117+
## References
118+
119+
- [vm-code-upgrade.md](vm-code-upgrade.md) — planned upgrade & rollback
120+
- [derot-key-issuance.md](derot-key-issuance.md) — how keys are issued
121+
- [secrets-management.md](secrets-management.md) — out-of-band secret injection
122+
- [SECURITY.md](../../SECURITY.md) — disclosure policy
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Secrets Management
2+
3+
How runtime secrets reach the Lit Chipotle CVM, what the attestation does and does
4+
**not** cover about them, and the policies that govern them.
5+
6+
> **Status:** the mechanism below is accurate to `docker-compose.phala.yml`. The
7+
> policy items (who can set/read, rotation cadence, dev/prod separation) are marked
8+
> **TODO/DECIDE** where not yet finalized.
9+
10+
## The important caveat: secrets are outside the attestation
11+
12+
Attestation proves *what code* the enclave runs (the compose hash in RTMR3) and *what
13+
OS* it booted. It does **not** measure the values of injected environment variables.
14+
Secrets are supplied to the CVM as **encrypted Phala CVM environment variables**,
15+
resolved by Phala at container startup and decrypted inside the TEE.
16+
17+
Consequences a reviewer should understand:
18+
19+
- The compose hash pins the *names and wiring* of env vars (they appear in the
20+
digest-pinned `docker-compose`), but **not their values**. Changing a secret's
21+
value does not change the compose hash.
22+
- Whoever controls the Phala account (the `PHALA_CLOUD_API_KEY`) controls what gets
23+
injected. This is why that key is a high-value target — see
24+
[incident-response.md](incident-response.md) Scenario 3.
25+
- The secrets are protected *in transit and at rest* by Phala's encrypted-env
26+
mechanism and only decrypted inside the attested enclave; they are not exposed to
27+
the host. But their *provenance* is not part of the on-chain trust chain.
28+
29+
## Secrets injected today
30+
31+
From `docker-compose.phala.yml` (all set as encrypted Phala CVM env vars):
32+
33+
| Secret | Used by | Purpose | Blast radius if leaked |
34+
|---|---|---|---|
35+
| `BASE_CHAIN_RPC` | lit-api-server | Base RPC endpoint | Low (read-mostly endpoint) |
36+
| `STRIPE_SECRET_KEY` | lit-api-server | Stripe billing (live in prod) | **High** — payments |
37+
| `STRIPE_PUBLISHABLE_KEY` | lit-api-server | Stripe client key | Low (publishable) |
38+
| `GCP_SERVICE_ACCOUNT_JSON` | otel-collector | Ship telemetry to GCP | Medium — scoped to telemetry project |
39+
| `GCP_PROJECT_ID` | otel-collector | GCP target project | Low |
40+
| `CERTBOT_AWS_ACCESS_KEY_ID` / `…SECRET_ACCESS_KEY` / `…ROLE_ARN` | dstack-ingress | Route53 DNS-01 for TLS | **High** — DNS control; scope to DNS-01 only |
41+
42+
## Policies
43+
44+
- **Least privilege per credential.** Each credential should be scoped to exactly its
45+
function (e.g. the Route53 IAM principal limited to DNS-01 TXT records on the cert
46+
domain; the GCP service account limited to telemetry ingestion). **TODO/DECIDE:**
47+
confirm and document the IAM/role scopes actually in use.
48+
- **Dev vs prod separation.** Dev uses Stripe sandbox keys and the dev environment;
49+
prod uses live keys. **TODO/DECIDE:** confirm separate Phala accounts/projects and
50+
that no prod secret is reachable from a dev deploy.
51+
- **Who can set/read.** Setting CVM secrets requires the Phala account credentials.
52+
**TODO/DECIDE:** name the owning role and how access is granted/revoked.
53+
- **Rotation.** **TODO/DECIDE:** define rotation cadence per credential and the
54+
rotate-then-redeploy procedure (see incident runbook Scenario 5).
55+
56+
## Telemetry egress note
57+
58+
The `otel-collector` ships logs/metrics to GCP from inside the CVM — a data-plane
59+
egress out of the TEE. Ensure exported telemetry contains **no secrets or request
60+
plaintext** (only operational metrics and scrubbed logs). **TODO/DECIDE:** document
61+
the scrubbing/allow-list applied before export.
62+
63+
## References
64+
65+
- `docker-compose.phala.yml` — the source of truth for injected env vars
66+
- [deployment.md](deployment.md) — required CI secrets/variables
67+
- [incident-response.md](incident-response.md) — secret-compromise response
68+
- [Phala: CVM environment variables / secrets](https://docs.phala.com/)

architectureDocs/deployment/vm-code-upgrade.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,14 @@ flowchart TB
5454
2. **Compute compose-hash** — dstack hashes the full `docker-compose.yml` (with pinned image
5555
digests) to produce the compose-hash that will appear in RTMR3 at boot.
5656

57-
3. **Governance approval** — The DstackApp owner (wallet, multisig, or timelock) submits an
58-
on-chain transaction to whitelist the new compose-hash. A timelock or multisig introduces a
59-
mandatory delay, giving stakeholders time to review before the new code can receive keys.
57+
3. **Governance approval** — The DstackApp owner submits an on-chain transaction to whitelist
58+
the new compose-hash. For the **production hosted deployment** this owner is a **2-of-4 Safe
59+
multisig** (`0xF688…1098`) with **no timelock** — an approved change takes effect as soon as a
60+
quorum executes it. Self-hosters can configure their own owner (wallet, higher-threshold
61+
multisig, timelock, or DAO) to add a mandatory review delay. What signers verify before
62+
approving (provenance via Sigstore, reproducible compose-hash, diff review) and the live
63+
governance facts are documented in the published
64+
[Upgrade Governance](../../docs/architecture/verification/upgrade-governance.mdx) page.
6065

6166
4. **Deploy** — After the whitelist tx is confirmed, CI redeploys (`phala deploy` or DeRoT
6267
equivalent) with the new image digest substituted into docker-compose.

0 commit comments

Comments
 (0)