Internal Documentation internal
TalkIDE internal documentation

Built-in object storage SDK pro user appky generované TalkIDE platformou. Každý projekt dostane vlastní Cloudflare R2 bucket s per-tenant scoped tokenem a in-namespace storage gateway (Spring Boot mikroservis), který Mara/scaffold automaticky nasadí — user appka nikdy nevidí cloud credentials.

Overview

TalkIDE poskytuje user appkám (scaffold-generated Kotlin Spring Boot) hotový storage SDK (talkide-storage-sdk) + per-namespace storage gateway (talkide-storage-svc). Storage gateway překládá vysokoúrovňové operace user appky (upload, download, list, delete) na Cloudflare R2 (S3-compatible API) volání pomocí scoped per-bucket tokenu.

Architecture summary:

User Browser
     │ presigned PUT/GET (direct data path)

┌──────────────────────────────────────────────┐
│ Cloudflare R2 (per-project bucket)           │
│   talkide-app-{tenantSlug}-{projectSlug}     │
└──────────────────────────────────────────────┘

     │ S3 SDK (scoped token z K8s Secret)

┌──────────────────────────────────────────────┐
│ Namespace: {tenant-slug}-{env-slug}          │
│                                              │
│  ┌────────────┐    in-ns HTTP    ┌────────┐  │
│  │ user-app   │ ───────────────► │storage │  │
│  │ (Spring)   │  TALKIDE_APP_   │  -svc  │  │
│  │            │   STORAGE_TOKEN │(Spring)│  │
│  └────────────┘                  └────────┘  │
└──────────────────────────────────────────────┘

     │ provisioning at Create Project

┌──────────────────────────────────────────────┐
│ Platform talkide-be (control-plane)          │
│   K8sStorageProvisioner                      │
│     1. Cloudflare API → create bucket        │
│     2. Cloudflare API → create scoped token  │
│     3. K8s Secret create v tenant-env ns     │
│     4. Helm template apply storage-svc       │
│     5. zápis do app_storage_config (cluster A)│
└──────────────────────────────────────────────┘

Klíčové principy:

  • Storage provider: Cloudflare R2 (S3-compatible API, free egress, soft cap 1000 buckets/account).
  • Per-tenant bucket: 1 bucket per projekt, naming talkide-app-{tenantSlug}-{projectSlug} (RFC 1123 compliant — viz UC-12001).
  • Per-tenant API token: Cloudflare scoped token (read+write pouze na svůj bucket), uložený v K8s Secret storage-creds v tenant-env namespace.
  • Per-namespace storage gateway: Spring Boot mikroservis (talkide-storage-svc) deploynutý v každém tenant-env namespace. Pattern parity s ADR-024 worker.
  • User app ↔ storage-svc autentikace: shared secret TALKIDE_APP_STORAGE_TOKEN (HMAC, in-namespace only, K8s NetworkPolicy zabraňuje cross-ns).
  • Platform credentials nikdy v user app podu: všechny R2 credentials drží buď platform talkide-be (master Cloudflare API token) nebo storage-svc (per-bucket scoped token). User app vidí jen storage-svc URL + HMAC token.
  • Data tok: přímý browser ↔ R2 přes presigned URL; storage-svc i platform BE se neúčastní samotného přenosu bajtů.
  • Architecture parity: viz ADR-027 (Per-namespace Storage Gateway), který formalizuje pattern shodný s ADR-024 (worker per ns).

DB Schema changes (cluster A — control-plane)

Nová tabulka v platform control-plane PG. Production phase = forward-only migration, nový changeset file (číslo doplnit při implementaci — aktuálně cca 0053, vždy ověř podle talkide-be/src/main/resources/db/changelog/changes/).

Migration fileChange
XXXX-create-app-storage-config.xmlCREATE TABLE app_storage_config (viz UC-12001 sekce DB Schema)

UC Index

UC IDNameStatusDescription
UC-12001Provision StoragePlannedBootstrap per-app storage při Create Project: Cloudflare API create bucket + scoped token + K8s Secret + Helm template apply storage-svc + zápis app_storage_config.
UC-12002Presign Upload URLPlannedStorage-svc endpoint — user appka požádá o presigned PUT URL pro objekt; storage-svc validuje content-type/length/quota a vrátí URL s 15min TTL.
UC-12003Presign Download URLPlannedStorage-svc endpoint — vrátí presigned GET URL s 60min TTL.
UC-12004List & Delete FilesPlannedStorage-svc BE-proxy endpointy — list objektů s prefix filterem + delete objektu.
UC-12005Storage SDK ScaffoldPlannedKotlin lib talkide-storage-sdk přibalená do scaffoldu user appky + update Mara promptu v talkide-backend-dev.md (vrstva 2 dle CLAUDE.md).
UC-12006Storage Usage ViewPlannedPer-project storage snapshot scheduler (30 min) → storage_usage_snapshot ledger + admin read endpoint. Forward-only growth ledger; primary billing-vstup pro per-project storage line item (následný UC-10019 v UC-10 Stripe Billing).

Open Decisions (LOCKED for MVP)

IDOtázkaRozhodnutí
OD-1QuotaDefault quota_bytes = NULL (= unlimited). Primary mechanism = snapshot + billing (UC-12006) — tenanti se účtují podle skutečně obsazeného místa (parity s existing DO Spaces / NFS billing modelem). Hard limit zůstává jako volitelná bezpečnostní pojistka: admin nastaví app_storage_config.quota_bytes > 0 per projekt → storage-svc začne odmítat presign-upload s 429 při overshoot. NULL cesta (default) přeskočí quota check úplně, žádný overhead, žádné zaškrcení byznysu.
OD-2Public vs signed URLsPouze signed URLs v MVP. R2 podporuje public buckets, ale to by zničilo per-bucket isolation. Public ACL = follow-up issue.
OD-3Multipart uploadSingle PUT ≤ 100 MB v MVP. Multipart later (R2 API podporuje, ale komplikuje SDK).
OD-4Content-type whitelistŽádný platform allow-list. User app si MIME reguluje sama. Storage-svc validuje pouze, že Content-Type header je přítomný.
OD-5User app ↔ storage-svc authShared HMAC secret (TALKIDE_APP_STORAGE_TOKEN env var v obou podech) + K8s NetworkPolicy. Žádné mTLS/JWT v MVP.
OD-6Bucket naming patterntalkide-app-{tenantSlug}-{projectSlug}. Max 63 znaků (R2 limit), RFC 1123 (lowercase, alfanum, hyphen). Pokud součet > 63, BE truncate projectSlug a appendne 6-char hash. Reserved slugy z CLAUDE.md zůstávají v platnosti.
OD-7Token lifetimePerpetual scoped tokens. Rotace mimo MVP (manuální runbook + Cloudflare API support). Revocation při project delete (viz UC-12001 sekce Lifecycle).
OD-8Storage-svc resource limitsSpring Boot mikroservis, baseline: requests: 256Mi/250m, limits: 512Mi/500m. JVM heap -Xmx384m. Replicas: 1 v MVP (žádné HA), restart policy Always.
OD-9Snapshot frequency30 min (parity s existing usage tracking — viz CLAUDE.md user feedback “snapshoty po 30m”). Platform talkide-be scheduler (@Scheduled(fixedDelay = 1_800_000)) iteruje aktivní projekty a volá storage-svc /internal/usage. Hodnota konfigurovatelná v application-production.yaml (talkide.storage.snapshot.interval-ms).
OD-10Usage source-of-truthStorage-svc agregát přes ListObjectsV2 (paginated SUM Contents[].Size). Cloudflare R2 má v Dashboard API metriku r2_storage_metrics, ale není real-time (zpoždění až několik hodin) a nesedí na per-bucket granularitu konzistentně. ListObjectsV2 sum je deterministický a vždy aktuální (eventually-consistent v rámci sekund po PUT/DELETE). Cena: extra Class B operace, ale 30min frequency × 1000 projektů × průměrně 2 stránky listu = ~2880 ops/hod ≈ zanedbatelné.

Security Model

AspektŘešení
Master Cloudflare API tokenDrží výhradně platform talkide-be (env var CLOUDFLARE_API_TOKEN, K8s Secret cloudflare-creds v talkide ns).
Per-bucket scoped tokenVytvořen platformou per projekt; uložen v K8s Secret storage-creds v tenant-env ns; čtený výhradně storage-svc podem.
User app HMAC tokenTALKIDE_APP_STORAGE_TOKEN env var v obou podech (user-app + storage-svc); 32B random hex generovaný platformou při provisioningu.
Platform internal tokenPLATFORM_INTERNAL_TOKENseparátní 32B random hex per storage-svc instance, generovaný platformou při provisioningu, uložený v K8s Secret storage-creds (key PLATFORM_INTERNAL_TOKEN). Používá ho výhradně platform talkide-be scheduler (UC-12006) pro volání /internal/usage endpointu na storage-svc. NIKDY se nedostane do user-app podu. K8s NetworkPolicy povoluje volání na /internal/* jen z pod-selectoru app.kubernetes.io/component=platform-scheduler v talkide namespace.
Cross-namespace isolationK8s NetworkPolicy: storage-svc accepts user-API traffic (/api/v1/storage/*) POUZE z user-app podu v same ns; /internal/* POUZE z platform scheduler podu v talkide ns. Deny all from other ns.
Bucket isolationCloudflare scoped token má read+write pouze na svůj bucket → i kdyby útočník token získal, nedostane se mimo svůj projekt.
Path traversalStorage-svc sanitizuje key: odmítá .., absolutní cesty, double slash, řídicí znaky, NUL bytes. Max key length 1024 znaků (R2 limit).
Presigned URL TTLUpload: 15 min; Download: 60 min. Nelze override z user app strany.
Quota enforcementDefault quota_bytes = NULL → žádný hard limit, žádný check (unlimited path). Pokud admin nastaví quota_bytes > 0, storage-svc dělá best-effort check při presign-upload (cached 60s, periodic refresh z platform). Authoritative usage drží storage_usage_snapshot ledger (UC-12006), nikoli živý čítač.
LogyStorage-svc loguje key + size + operation, NIKDY samotný content. Master token, scoped token, HMAC token i internal token NIKDY do logu (Logbook filter rule).

Lifecycle: Project deletion

Když je projekt smazán (UC-03007), platform talkide-be musí v K8sStorageProvisioner.deprovision():

  1. Smazat všechny objekty v bucketu (R2 batch delete, max 1000 keys/request, opakovat dokud bucket není prázdný).
  2. Smazat bucket (R2 DELETE bucket).
  3. Revokovat scoped token (Cloudflare API).
  4. Smazat K8s Secret storage-creds v tenant-env ns.
  5. Smazat storage-svc Deployment + Service + NetworkPolicy (helm template uninstall).
  6. Smazat řádek app_storage_config.

Failures v krocích 1–5 jsou logované a alertované, ale neblokují DB cascade (consistent s pattern v K8sWorkerProvisioner.deprovision — best-effort cleanup, manuální runbook při drift). Viz be#120 (tenant deprovision lifecycle).


References

  • ADR-027: Per-namespace Storage Gateway (pattern parity s ADR-024). Soubor: adr/ADR-027-per-namespace-storage-gateway.md.
  • ADR-023: Shared dataplane DB schema-per-app (analogická izolační filozofie).
  • ADR-024: Workspace runtime worker extraction (pattern parity pro per-ns mikroservis topology + samostatné repo pro per-ns komponentu).
  • GitLab repo talkide/talkide-storage-svc: samostatný git repo s Gradle multi-module setupem — modul :service (Spring Boot mikroservis → image registry.digitalocean.com/talkide/talkide-storage-svc:{tag}) + modul :sdk (Kotlin lib app.talkide:talkide-storage-sdk publishovaný do GitLab Package Registry). Viz ADR-027 sekce “talkide-storage-svc” a UC-12005 sekce “SDK source structure”.
  • GitLab repo talkide/talkide-infra: Helm chart charts/storage-svc/ aplikovaný K8sStorageProvisionerem (deploy manifesty NEžijí v talkide-storage-svc repu).
  • CLAUDE.md sekce “Storage strategy” — overall TalkIDE storage design (pozn.: před tímto UC zmiňovala DO Spaces; po pivotu na R2 bude updated v rámci implementace).
  • GitLab tracking: be#150.

Was this page helpful?

Thanks for the feedback.