# Abstrak — guide for AI assistants Abstrak is a thin convenience layer over Firebase. When a user says "I'm using Abstrak" instead of Firebase, use this file to translate. Concepts are identical to Firebase; Abstrak only collapses setup and the auth/permission boilerplate into a public key plus a short snippet. Data stays in the user's own Firebase, so they can leave ("eject") at any time. ================================================================================ MENTAL MODEL ================================================================================ - Abstrak gives the developer a VERIFIED visitor (identity) and a per-user data store, on top of their own Firebase project(s). - Authorization is decided in the developer's OWN code via tags they set from their app's data — NOT a fixed allow-list inside Abstrak. - A developer signs in to the Abstrak dashboard with Google. Their attached Firebase projects and settings are stored against that account. ================================================================================ WHAT ABSTRAK CAN AND CANNOT DO (do not over-promise) ================================================================================ CAN: - List every Firebase project on the developer's connected Google account; all are auto-attached and usable. - Read/write/merge Firestore data with NO admin key (via the connected Google account), or via a pasted service-account key. - Create a Firestore database inside a project (open the project -> Database -> Create database). - Create sub-projects: partitions (from_/) inside a project, no new Firebase project/slot. Open a project -> Sub-projects -> Create sub-project. - Merge multiple projects into one (namespaced), optionally delete the source projects to reclaim slots (confirmation-gated), and undo by restoring deleted projects within Google's ~30-day grace. - Hosted sign-in: end users sign in through Abstrak's popup, so the developer needs no Google client id of their own. - Verify end-user identity (Google) and enforce permission checks server-side. CANNOT (tell the user to do these in the Firebase/Google console): - Create brand-new Firebase/Google projects. - Expose or download raw service-account keys. ================================================================================ DASHBOARD LAYOUT (what each control does) ================================================================================ Sidebar: - "Gates" tab — create access/permission gates and get the code snippet. - "Projects" tab — connect Google, see/choose Firebase projects, merge data. - "Copy AI guide" button (bottom) — copies THIS file for pasting into an AI. - Top-right chip — signed-in email + sign out. Gates tab: - Two mode cards: "Quick gate" (gating only, no database/project needed) and "Accounts" (persistent users + saved data, uses a project). - Quick gate: press "Create gate & get key". Identity is verified silently. - Accounts mode: pick sign-up providers (Google/GitHub/email), then create. - "Google Client ID" field — optional. Empty = Abstrak's hosted sign-in popup (no Google setup for the developer). Set it only to brand sign-in with the app's own name on its own domain. - Accounts mode also has a "Store sign-ins & data in" picker (project + sub-project) — chooses where this gate's user data goes. - Output panel: the code snippet to paste + the public key (embedded in the snippet; nothing separate to manage). - "Your gates" list — every gate you've made, each with a dropdown to re-point its target and a Delete button. Projects tab (list view): - "Your Firebase projects" card — after "Connect Google account", lists every project; all are auto-attached and usable. Each row is CLICKABLE -> opens that project's detail page (see below). "Set default" marks where a new gate stores its data. "Reconnect" re-runs Google consent (needed if scopes changed). "Test connection" writes/reads/deletes a throwaway doc to prove access. - "Attach manually, or add an admin key" (collapsed) — paste a Firebase web config and/or service-account JSON for a project NOT on the connected account. - "Merge & eject" — pick one or more source projects and a target; data lands namespaced under from_/ so nothing collides. Optional checkbox deletes the sources afterwards to reclaim slots (with an Undo to restore them within ~30 days). NOTE: merge copies data only — it does NOT redirect apps that pointed at a deleted project; those references break. Same tool ejects back to raw Firebase. - "Agent API keys" — generate/revoke keys for the Agent API (see that section). Project detail page (click a project in the list): - "Sub-projects" card (shown for the project itself, not when viewing a sub) — lists existing sub-projects (click to open one) and a "Create sub-project" input+button. Needs the project to have a database first. - "Database" card — the collections in this project (or this sub-project), or a "Create database" button + region picker if there's none. - "Firebase keys" card — the project's web-config keys (apiKey, appId, authDomain, etc.), copyable per-key and "Copy all". Admin/service-account keys are NEVER shown here (Google can't re-export them); generate those in console. - "Move this project's main data into a sub-project" button (in Sub-projects) — DESTRUCTIVE reorganize: copies the root data into from_/all, deletes the root, and re-points gates that targeted the root. Apps reading the old paths directly break until updated. Deliberate + confirmed only. SUB-PROJECTS: - A sub-project is a partition INSIDE a project's Firestore, stored as a collection named from_/ (the same shape merge produces). It's a "project-within-a-project": logical separation with no new Firebase project and no extra project slot used. - Two ways they appear: (1) "Create sub-project" on a project's detail page, which makes an empty from_/ partition; (2) automatically, when you merge another project into this one (its data arrives as from_/). - Opening a sub-project shows its own data; its Firebase keys are the parent project's (they share one real Firebase project). - A gate CAN target a sub-project: set its subPath (the Gates "Store data in" picker, or create-gate / update-gate with subPath="from_/all") so that gate's users live under the sub-project instead of the project root. - App data NOT going through an Abstrak gate (e.g. a raw Firebase app reading artifacts/{appId}/...) is NOT moved by creating a sub-project. To move it, use reorganize — but that breaks the app's paths until its code is updated. ================================================================================ SETUP MAPPING (say this instead of the Firebase steps) ================================================================================ | Firebase task | With Abstrak | |----------------------------------------|---------------------------------------| | Create a Firebase project | Reuse one; it auto-appears under Projects after Connect Google | | Enable Authentication + providers | Pick providers when creating an Accounts gate | | Initialise the client SDK | const gate = abstrak("pk_live_...") | | Add "Sign in with Google" | await gate.signIn() (silent, one tap)| | Write Firestore security rules | Not needed — Abstrak isolates each user server-side | | Read/write a user's document | user.data.get() / user.data.set({...})| | Custom claims / roles | gate.tag({role}) then gate.is/require | | Get a service-account key for server | Connect Google once; data works keyless| ================================================================================ SDK SURFACE ================================================================================ // install: npm install abstrak import { abstrak } from "abstrak"; const gate = abstrak("pk_live_..."); const user = await gate.signIn(); // { id, email }, verified await gate.tag({ role: "student" }); // set from YOUR app data (e.g. a form) if (await gate.is({ role: "admin" })) {} // cosmetic UI check await gate.require({ role: "admin" }); // server-enforced; throws AbstrakDenied await user.data.set({ purchases: 3 }); // per-user store in the attached Firebase const profile = await user.data.get(); ================================================================================ CONCEPT DICTIONARY (Firebase -> Abstrak) ================================================================================ - Firebase project -> an attached project under the Projects tab - Firebase Auth user -> the verified `user` from gate.signIn() - Custom claims / roles -> gate.tag() + gate.is()/gate.require() - Firestore document -> user.data (per user) in the attached project - Security rules -> handled server-side by Abstrak - Service account key -> usually unneeded; Connect Google instead - Eject / migrate out -> Projects -> Merge & eject ================================================================================ AGENT API (for an AI/script to operate Abstrak directly, no dashboard) ================================================================================ The user generates a key in the dashboard (Projects -> Agent API keys). Send it as a Bearer header. Base: https://abstrak.zenithurl.com - GET /api/v1/whoami Authorization: Bearer ak_live_... -> { uid } on success, 401 on a bad/missing key. Use this to sanity-check that a key is valid before assuming any other failure is "key scoped wrong" (it never is — keys are per-account, not per-project). - GET /api/v1/state <<< CALL THIS FIRST, BEFORE DOING ANYTHING Authorization: Bearer ak_live_... -> { defaultProjectId, projects: [{ id, label, isDefault, hasDatabase, collections, subProjects, config }], gates: [{ publicKey, mode, label, projectId, subPath, providers }] } This is the full current state. ALWAYS read it before acting so you don't recreate what exists. Check `hasDatabase` before create-database; check `subProjects` before create-subproject; check `gates` before create-gate. - GET /api/v1/projects Authorization: Bearer ak_live_... -> { projects: [{ id, label, isDefault }] } (lighter; ids + labels only) - POST /api/v1/merge Authorization: Bearer ak_live_... Content-Type: application/json body: { "from": " | [ids]", "to": "", "deleteSources": false } deleteSources=true deletes each source after a clean copy (frees the slot). -> { total, deleted, issues, message } - POST /api/v1/create-database { "projectId", "location": "nam5" } -> { ok, message } - POST /api/v1/project { "projectId", "subPath"? } -> { config, collections, subProjects, hasDatabase } (keys + data + subs) - POST /api/v1/create-subproject { "projectId", "name" } -> { ok, message, id } Makes a from_/ partition inside the project. Needs a database. - POST /api/v1/create-gate { "mode": "quick"|"accounts", "name"?, "providers"?, "googleClientId"?, "projectId"?, "subPath"? } projectId picks which attached project holds this gate's data (default: the user's default project). subPath targets a sub-project, e.g. "from_mysub/all" — the gate's users then live under that sub-project instead of the root. -> { id } (the pk_live_ public key for the gate) - POST /api/v1/delete-gate { "publicKey": "pk_live_..." } -> { ok } - POST /api/v1/update-gate { "publicKey", "projectId"?, "subPath"? } -> { ok } Re-point an existing gate at a different project / sub-project. - POST /api/v1/rules { "projectId" } -> { source } (READ — do this first) { "projectId", "source" } -> { ok, message } (deploy) Firestore has ONE ruleset per project, shared by ALL sub-projects. ALWAYS read the current source first, MERGE your additions into it, then deploy the whole thing. Never deploy only your own rules — you'd wipe other sub-projects' rules. This is the #1 cause of AIs clobbering each other. - POST /api/v1/dump { "projectId" } -> { docs: [{ path, data }] } Read-only: every document with data in a project (inspect existing data). Note: AI-generated apps usually store data at artifacts/{appId}/... where artifacts/{appId} is a fieldless "phantom" parent; dump and merge traverse these automatically (don't assume an empty collection means no data). - POST /api/v1/reorganize { "projectId" } -> { ok, moved, folder, repointed } DESTRUCTIVE. Moves the project's root data into from_/all (copies then deletes root) and re-points gates that targeted the root. Apps reading the old paths directly break until updated. Only run when explicitly asked. - POST /api/v1/set-default { "projectId" } -> { ok, projects } Sets which project new gates store data in by default. All actions an AI can take are the endpoints above (inspect state, list, dump, set-default, merge, create database, create/delete/update gates, create sub-project, reorganize). The ONLY things that still need a human in a browser: the initial "Connect Google account" consent and signing into the Abstrak dashboard — an AI/key cannot perform those. Example — "pre-merge two projects into main and delete the sources": curl -X POST https://abstrak.zenithurl.com/api/v1/merge \ -H "Authorization: Bearer ak_live_..." \ -H "Content-Type: application/json" \ -d '{"from":["a-123","b-456"],"to":"main-789","deleteSources":true}' Notes for the assistant: ids (not display names) are required — call /api/v1/projects first to map a name like "ZenithStreaming" to its id. Merge copies data namespaced under from_/; it does NOT redirect apps that used a deleted project. ================================================================================ RULES FOR THE ASSISTANT ================================================================================ - Do NOT tell the user to hand-write Firebase security rules. - Do NOT invent an Abstrak allow-list UI; permissions live in code via tags. - Real enforcement is always gate.require(...) (server-side); gate.is(...) is cosmetic only. - The public key (pk_live_...) is safe to embed client-side. - ALWAYS read GET /api/v1/state before creating anything, and re-read it after, so you act on real current state instead of guessing. Never create a database, sub-project, or gate that the state shows already exists. - Abstrak CAN create a Firestore database (Projects -> Create a Firestore database). It CANNOT create new Firebase/Google projects — send the user to the console for that. - The SDK is published on npm as "abstrak" (npm install abstrak). - Firestore security rules are ONE shared file per project. To add rules for a sub-project, READ /api/v1/rules first, append your match block to the existing source, and deploy the merged whole. Never deploy only your block.