claude-timemachine 2b7290626e
CI / validate (push) Successful in 13s
CI / docker (push) Successful in 14s
server: extract tarball into blob store on snapshot upload
The deferred 'hardlink blobs from tarball' optimization from DESIGN.md
landed as 'just walk the tarball and write blobs separately' for v1.
GET /v1/blob/{sha} was 404'ing because the blob store was empty —
storage only had snapshots/<id>.tar.zst and a manifest.

Server now:
1. Parses uploaded multipart manifest + tarball
2. Walks the tar entries, computes each entry's sha256
3. Cross-checks against the manifest's declared sha (rejects 400 on mismatch)
4. Writes each blob to <user>/blobs/ via Storage.WriteBlob
5. Then stores the snapshot tarball + manifest as before

2 new tests cover: (a) POST then GET /v1/blob/{sha} round-trip,
(b) manifest-claims-different-sha-than-tarball rejection.

Discovered via e2e smoke against frazclient: pull 404'd on every blob
after a successful push. 33/33 tests pass.
2026-06-02 19:08:56 +02:00

cloud-svc

Steam-Cloud-style per-user file sync for Minecraft clients. Pull on launch, push on exit, with per-file mtime conflict resolution + N-snapshot history.

Part of the automc platform. See DESIGN.md for architecture.

Status

Skeleton. Code, tests, build all working. Not yet deployed.

  • HTTP API (7 endpoints)
  • On-disk blob + tarball + manifest storage
  • Token verification via auth-service (with 60s cache)
  • Dev mode (accept any bearer)
  • Quota enforcement
  • Pg-backed per-user quota override (currently DefaultQuota only)
  • Snapshot retention / GC of unreferenced blobs
  • Tarball-vs-manifest content cross-check on push

Build + test

make build
make test
make vet

31 tests, 0 fails.

Run locally (dev mode)

make dev

Listens on 127.0.0.1:9091. Accepts ANY non-empty bearer token; the token becomes the user ID. Files land under ./data/.

Run against real auth-service

CLOUD_LISTEN=127.0.0.1:9091 \
CLOUD_STORAGE_ROOT=/var/lib/cloud-svc/data \
CLOUD_AUTH_SERVICE_URL=http://auth-service:9090 \
CLOUD_SERVICE_KEY=$(cat /etc/cloud-svc/service-key) \
CLOUD_DEFAULT_QUOTA_MB=200 \
./cloud-svc

API quick reference

Method Path Purpose
GET /v1/manifest Latest manifest for caller
GET /v1/blob/{sha256} Raw file content
POST /v1/snapshot Multipart upload: manifest + tarball
GET /v1/snapshots List caller's snapshot history
GET /v1/snapshot/{id} Historical tarball
DELETE /v1/snapshot/{id} Remove (latest protected)
GET /v1/quota {used_bytes, limit_bytes, snapshots}
GET /healthz Unauthenticated liveness probe

All authenticated endpoints require Authorization: Bearer <token> with cloud:rw scope.

See DESIGN.md for full details + on-disk layout + conflict semantics.

Configuration (env)

Var Default Purpose
CLOUD_LISTEN 127.0.0.1:9091 HTTP bind address
CLOUD_STORAGE_ROOT /data Root dir for blobs + snapshots
CLOUD_AUTH_SERVICE_URL http://auth-service:9090 auth-service base URL
CLOUD_SERVICE_KEY (required when not dev) cloud-svc's own X-API-Key for calling auth-service
CLOUD_AUTH_CACHE_TTL 60s Verified-token cache TTL
CLOUD_DEFAULT_QUOTA_MB 200 Per-user quota (MB)
CLOUD_DEV_MODE unset If 1: accept any bearer; bypass auth-service
S
Description
Steam-Cloud-style per-user state sync for Minecraft clients. Part of the automc platform. Pulls/pushes per-Discord-user file state with per-file mtime conflict resolution + N-snapshot history.
Readme 60 KiB
Languages
Go 98.7%
Dockerfile 0.7%
Makefile 0.6%