From 490e7adcf7001100b0799256a820d922d3431664 Mon Sep 17 00:00:00 2001 From: Sean Dean <254259913+distronode-com@users.noreply.github.com> Date: Fri, 4 Sep 2026 04:37:53 -0400 Subject: [PATCH 1/2] docs(deploy): document the native gcs:// replica alongside s3:// MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEPLOY.md described LITESTREAM_REPLICA_URL as `s3:///calnode` and the backup section covered only S3-compatible providers, so a GCP deployer had no reason to know the bundled Litestream speaks GCS natively — which is the simpler option there, because credentials come from the instance metadata server and there is no access key to create, store or rotate. Verified against the litestream binary in the published image rather than inferred from its docs: - the scheme is `gcs://`. `gs://` — the scheme every other Google tool uses — is rejected with `unknown replica type in config: ""` and exit 1, before any network call, so it presents as neither a credentials nor a bucket problem. That is the one thing most likely to cost someone an hour, so it leads. - the bundled /etc/litestream.yml needs no edit: its `endpoint:` and `region:` lines expand to empty strings and a gcs replica ignores both. - no LITESTREAM_ACCESS_KEY_ID / _SECRET_ACCESS_KEY are needed. That last point is also a trade worth stating, and it is the part a deployer can only discover by looking at the code: recordingStorage() in internal/handler/livekit_recording.go reuses the backup bucket for meeting recordings over the S3 API and requires both credential variables, so a native GCS replica leaves recording storage unavailable. It degrades cleanly — Settings → Storage reports recordings_storage_ready: false and the Record button stays hidden — but it is a choice, so the section says so and points back at the S3 route for anyone who wants recording. The S3 documentation is unchanged; this is added beside it. The two enabling steps that assumed five variables now name the GCS case in one clause each. --- DEPLOY.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/DEPLOY.md b/DEPLOY.md index e5e6297..a5483d5 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -161,7 +161,7 @@ variables cover every provider — only the endpoint/region change: | Variable | Purpose | |---|---| -| `LITESTREAM_REPLICA_URL` | `s3:///calnode` — the bucket + path. Setting this turns backups ON. | +| `LITESTREAM_REPLICA_URL` | `s3:///calnode` — the bucket + path. Setting this turns backups ON. (For native Google Cloud Storage use `gcs://` — see below.) | | `LITESTREAM_ENDPOINT` | Provider S3 endpoint. **Leave unset for AWS.** | | `LITESTREAM_REGION` | Bucket region (`auto` for R2). | | `LITESTREAM_ACCESS_KEY_ID` | Access key (use a bucket-scoped key, not a root credential). | @@ -186,9 +186,40 @@ Per-provider values (everything else is identical): If the endpoint/region are empty or wrong, Litestream silently falls back to **AWS** and you'll see `InvalidAccessKeyId` (403) in the logs (your R2 key sent to Amazon). +### Google Cloud Storage (native, not via the S3 API) + +The bundled Litestream also speaks **GCS natively**, which is the simpler option on GCP: +credentials come from the instance metadata server, so there is no access key to create, +store or rotate. One variable is enough: + +| Variable | Value | +|---|---| +| `LITESTREAM_REPLICA_URL` | `gcs:///calnode` | + +- ⛔ **The scheme is `gcs://`, not `gs://`.** `gs://` is the scheme every other Google tool + uses, and Litestream rejects it with `unknown replica type in config: ""` and exit 1 — + before any network call, so it looks nothing like a credentials or bucket problem. +- **Leave `LITESTREAM_ENDPOINT`, `LITESTREAM_REGION`, `LITESTREAM_ACCESS_KEY_ID` and + `LITESTREAM_SECRET_ACCESS_KEY` unset.** The bundled `/etc/litestream.yml` needs no edit: + its `endpoint:` and `region:` lines expand to empty strings, and a `gcs` replica ignores + both. +- The service account attached to the instance needs read **and** write on the bucket + (read is what restore uses). Everything else on this page — the restore drill, the + private-bucket warning, the PII note — applies unchanged. + +⚠️ **The trade: meeting recordings need the two S3 credential variables, so a native GCS +replica leaves recording storage unavailable.** `recordingStorage()` +(`internal/handler/livekit_recording.go`) reuses the backup bucket for recordings over the +S3 API and requires `LITESTREAM_ACCESS_KEY_ID` and `LITESTREAM_SECRET_ACCESS_KEY`; without +both it reports not-configured. It degrades cleanly rather than failing at record time — +**Settings → Storage** shows `recordings_storage_ready: false` and the room's Record button +stays hidden — but if you want built-in recording, choose the S3-compatible route above. + ### Enabling it 1. Create a **private** bucket and a **bucket-scoped** access key (read + write — read is needed for restore). -2. Set the five variables on the service (Railway → Variables, or your platform's equivalent). + *(Native GCS: create the bucket and grant the instance's service account read + write; there is no key.)* +2. Set the five variables on the service (Railway → Variables, or your platform's + equivalent) — or, for native GCS, just `LITESTREAM_REPLICA_URL`. 3. Redeploy. On boot Litestream initialises and the first snapshot uploads within ~10s. 4. **Verify the round-trip** before relying on it (below). From 417c7aac7b1b2524f7ab1f0630d3d538b47470e2 Mon Sep 17 00:00:00 2001 From: Sean Dean <254259913+distronode-com@users.noreply.github.com> Date: Fri, 4 Sep 2026 05:26:31 -0400 Subject: [PATCH 2/2] docs(deploy): give the GCP deployer a real choice for recording storage The native gcs:// section ended by telling anyone who wanted built-in recording to "choose the S3-compatible route above", but the provider table above has no GCS row, so that sentence dead-ends exactly the reader it is addressed to. Spell out the two options instead: move the replica to an S3-API bucket, or reach the same GCS bucket through Google's S3 interoperability API with HMAC keys and an s3:// URL. The second is the ordinary S3-compatible route with GCS as the provider rather than anything new, and it is marked as untested by us rather than presented as a verified configuration, because we run native gcs:// with recordings off. Also points at docs/VIDEO.md section 2, which already enumerates what recording needs from either route. Co-Authored-By: Claude Opus 5 (1M context) --- DEPLOY.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/DEPLOY.md b/DEPLOY.md index a5483d5..81d15b1 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -213,7 +213,21 @@ replica leaves recording storage unavailable.** `recordingStorage()` S3 API and requires `LITESTREAM_ACCESS_KEY_ID` and `LITESTREAM_SECRET_ACCESS_KEY`; without both it reports not-configured. It degrades cleanly rather than failing at record time — **Settings → Storage** shows `recordings_storage_ready: false` and the room's Record button -stays hidden — but if you want built-in recording, choose the S3-compatible route above. +stays hidden. + +The provider table above has no GCS row, so "use the S3-compatible route" is not by itself +an answer on GCP. The two real options: + +- **Put the replica on an S3-API bucket** (R2, B2, AWS, MinIO — the providers above) and + set all five variables. This is the route this page documents end to end. +- **Or reach the same GCS bucket through Google's S3 interoperability API**: an HMAC key + pair as `LITESTREAM_ACCESS_KEY_ID` / `LITESTREAM_SECRET_ACCESS_KEY`, + `LITESTREAM_ENDPOINT=https://storage.googleapis.com`, and an `s3://` replica URL. That is + the ordinary S3-compatible route with GCS as the provider, not the native `gcs://` one. + ⚠️ We have not exercised recording this way — we run native `gcs://` with recordings off — + so it is stated as the shape of the answer, not as a configuration we have verified. + +`docs/VIDEO.md` §2 lists exactly what recording needs from either route. ### Enabling it 1. Create a **private** bucket and a **bucket-scoped** access key (read + write — read is needed for restore).