← all documents · raw markdown · 11 KB

Google Drive as Server Cloud Storage — Spec

Status: Draft — Phase 0 helper shipped ( ools/oracle-discord/drive-oauth-setup.mjs) · Date: 2026-07-17 · Owner: Ryan

Goal: Use the dedicated Google account's 5TB Drive as the server's cloud

storage backend — per-user workspace files, message attachments, and per-user

memory .md files — with storage quotas enforced per paid tier.

---

1. Summary

The KAI server (command-center-server.mjs) gains a Drive storage module

that stores each user's files in their own folder inside the 5TB Drive, tracks

how many bytes each user has used, and refuses uploads that would exceed the

user's tier limit. Files are addressed by the real user id from the identity

isolation fix (v9.10 / 2026-07-17), so storage is correctly attributed per user.

This is additive. Nothing about the existing engine, Discord fleet, or dashboard

changes except new /api/storage/* endpoints and a new server module.

---

2. Account & auth model (the decision that drives everything)

The 5TB lives on a dedicated account you created for this. That's the clean

case: the server can act fully *as* that account with no risk to a personal

identity.

Chosen path: OAuth2 with a stored refresh token, scope drive.file.

Ranked, with why:

1. OAuth2 refresh token, drive.file scope — RECOMMENDED (high confidence).

The server authenticates as the dedicated account and can create/read/manage

*only the files it creates* (drive.file = least privilege; it cannot see

anything else in that Drive). Files count against the account's 5TB. One-time

consent → long-lived refresh token stored in .env.

2. OAuth2 with full drive scope — works, not recommended. Broader access

than needed. Only use if we later need to read files the server did not create.

3. Service account + Shared Drive — only if this becomes a Workspace account.

Personal Gmail accounts cannot host Shared Drives, so this is off the table

for the current dedicated Gmail. If you later move to Google Workspace, this

becomes the cleanest option (no refresh-token maintenance).

4. Plain service account (no shared drive) — RULED OUT. A service account has

its own ~15GB quota and separate billing; files it creates do not draw

from the 5TB. This is the common trap.

5. GOOGLE_API_KEY / Vertex ADC (already in .env) — RULED OUT for storage.

Neither carries a Drive scope; they cannot access Drive user storage.

---

3. One-time setup (YOU do this — I cannot log into your Google account)

This is the only part I can't do for you: creating the credential and doing the

consent both require signing into the dedicated Google account. Steps:

1. Enable the API. Google Cloud Console → the existing project (the one in

ANTIGRAV_PROJECT) → *APIs & Services → Library* → search **Google Drive

APIEnable**. (Reusing your Antigravity project is fine.)

2. OAuth consent screen. *APIs & Services → OAuth consent screen* → set it to

External, add the dedicated account's email as a Test user (so it works

without Google verification), add scope .../auth/drive.file.

3. Create credentials. *Credentials → Create credentials → OAuth client ID →

Application type: Desktop app.* Download the JSON — it contains

client_id and client_secret.

4. Get a refresh token (one time). I'll ship a tiny helper script

tools/oracle-discord/drive-oauth-setup.mjs. You run it once:

approve.

refresh token and prints it.

actually returned.

5. Paste 3 values into .env (see §4). Done — the server can now use the 5TB.

> Security: the refresh token is a long-lived secret. It lives only in

> tools/oracle-discord/.env (same as every other secret) — never printed,

> committed, or echoed.

---

4. New .env keys

GDRIVE_CLIENT_ID=<from the OAuth client JSON>
GDRIVE_CLIENT_SECRET=<from the OAuth client JSON>
GDRIVE_REFRESH_TOKEN=<from the one-time setup script>
GDRIVE_ROOT_FOLDER_NAME=KAI-Server          # optional; default "KAI-Server"
GDRIVE_ENABLED=1                            # kill-switch: 0 = disable cleanly

No new secrets are needed beyond these three real ones.

---

5. Server storage module — tools/oracle-discord/storage/drive.mjs

Uses the googleapis npm package (add to package.json). Singleton Drive client

built from the refresh token; token auto-refreshes in memory.

Drive layout:

KAI-Server/                (root app folder, id cached)
  users/
    <userId>/              (one folder per real user id)
      workspace/           (Builder+ workspace files)
      attachments/         (message/DM attachments)
      memory/              (per-user .md memory files — task #20)

Functions (all async, all keyed by the caller's real userId):

folder before serving — no cross-user reads).

the whole 5TB (ops dashboard).

Design notes:

it's inside users/<callerId>/. This is the storage-side equivalent of the

identity isolation fix — a user can never fetch another user's file id.

---

6. Per-tier quota model

Drive has no native per-subfolder quota, so the server enforces it: before an

upload, userUsageBytes(userId) + incoming.size must be ≤ the user's tier limit.

Usage is also cached in the user record for fast reads.

Proposed starting limits (adjust — see §10; total must fit inside 5TB):

| Tier | Storage/user | Notes |
|-------------|--------------|----------------------------------------|
| Free | 1 GB | attachments (no full workspace folder) |
| Explorer | 5 GB | paid tools + browser |
| Creator | 25 GB | workspace files |
| Builder | 75 GB | + Antigravity coding workspace |
| Team | 150 GB | Roundtable + Full Fleet |
| Enterprise | 1 TB | baseline; config can raise |

Capacity math: 5TB = ~5,000 GB. Mixed tiers leave comfortable headroom for a

few hundred paid users; the ops dashboard shows total 5TB usage via

accountQuota() so you see the ceiling coming.

Enforcement returns HTTP 413 {ok:false,error:'quota_exceeded',limit,used} so

the UI can show "You're out of space — upgrade your tier."

Tier limits live in one server constant (TIER_STORAGE_BYTES) next to the

existing plan/credits config, so they're one edit to tune.

---

7. New API endpoints (all behind currentContentUser → real user only)

POST   /api/storage/upload      (multipart or base64) → {fileId,size,link}
GET    /api/storage/list?sub=   → user's files in a subfolder
GET    /api/storage/file?id=    → download (ownership-checked)
POST   /api/storage/delete      {fileId} → trash
GET    /api/storage/usage       → {used, limit, tier, pct}
GET    /api/storage/account     (owner/admin only) → whole-5TB usage

These reuse the same auth wall and per-user identity as the DM/receipt work.

---

8. Consumers to wire (after the module lands)

1. Message / DM attachments — the "Photo" button and DM images upload to

attachments/, store the returned link in the post/message record.

2. Per-user memory .md (task #20) — write/read the user's memory file in

memory/, with the local device copy as the fast cache and Drive as backup.

3. Builder-tier workspace — the Antigravity workspace files for Builder users

live in workspace/ (ties into the "Builder tier → Antigravity session"

request).

---

9. Rollout phases

and deletes a 1KB probe file. Verifiable in isolation, touches nothing else.

---

10. Risks / gotchas (honest list)

tokens can expire after 7 days. Fix: move the consent screen to

"In production" (no Google verification needed for drive.file with a single

known user) — then the refresh token is long-lived. I'll flag this in the setup

steps so you don't get surprised in a week.

of band. Mitigation: periodic reconcile against Drive's actual per-folder

listing; usage cache TTL ~60s.

and cache; don't poll usage on every request (cache it).

this).

is locked, storage is unavailable. Acceptable for now; note it for scale.

attachments; if you later want fast public media, add signed Drive links or a

real object store (R2/S3) — but 5TB Drive is a great start.

---

11. Open decisions for you

1. Tier storage sizes — accept the §6 table or give me your numbers.

2. drive.file vs full drive scope — recommend drive.file (least

privilege). Confirm.

3. Attachment size cap per upload (e.g., 25MB Free → 250MB Builder?).

4. When to move the OAuth consent screen to "production" (do it during setup

to avoid the 7-day token expiry — recommended).

Once you've done Phase 0 and answered §11, I'll build Phase 1 and give you the

probe-file test to confirm the 5TB is live before wiring anything else.