initial: Steam-Cloud-style per-user state sync skeleton
CI / validate (push) Successful in 26s
CI / docker (push) Failing after 8s

HTTP API + on-disk storage + auth-service token verification + dev mode.
31 tests pass, vet clean. See DESIGN.md for the architecture and
README.md for the operator surface.

Pending: pg-backed per-user quota override, snapshot retention / blob GC,
tarball-vs-manifest content cross-check, end-to-end deploy on john.
This commit is contained in:
2026-06-02 18:52:25 +02:00
commit 1752ef05a6
16 changed files with 2039 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
# 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`](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
```fish
make build
make test
make vet
```
31 tests, 0 fails.
## Run locally (dev mode)
```fish
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
```fish
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`](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 |