Design doc: ../STORES.md
Adding the Org → Store → Documents layer. A store is the named collection you upload into and query against; it also carries the domain profile.
| Phase | Status |
|---|---|
| Phase 0 — Org → Documents (no stores) | Shipped (today) |
Phase 1 — CP + engine core (stores, store_id, default store) |
Not started |
| Phase 2 — dashboard (switcher, stores page, scoped views) | Not started |
| Phase 3 — SDKs + MCP store support | Not started |
| Phase 4 — profile-on-store, store-bound keys, per-store usage | Not started |
Everything is additive: a default store per org preserves today's
single-pool behavior with zero breakage.
The foundation. After this, every document silently belongs to a store; existing flows keep working via the default store.
- CP migration:
storestable (id, org_id, name, slug, description, profile, timestamps;UNIQUE(org_id, slug)).api_keys.store_idnullable FK.store_idon usage tables. - CP model:
model/store.go;StoreIDonAPIKey+Principal. - CP store layer: Create / Get / GetByOrgSlug / ListByOrg /
Update / Delete;
EnsureDefaultStore(orgID). - CP handlers + routes: CRUD under
/admin/v1/orgs/{orgId}/stores. - CP auto-default: call
EnsureDefaultStorewherever orgs are ensured (signup / login / me), mirroring auto-org. - CP auth:
APIKeyAuthresolvesapiKey.StoreID;SessionAuthreads + validatesX-Vectorless-Storeagainst the org. - CP proxy: inject
X-Vectorless-Storeinproxy.Forward(resolution: header → key binding → org default). - Engine migration:
documents.store_id+(org_id, store_id, created_at)index. - Engine db:
Document.StoreID;GetDocument/ListDocuments/DeleteDocument/CountSections/LoadTreefilter bystore_id;NewDocumentpersists it. - Server handlers:
requireStoreID+ thread into documents / query / connect handlers. - Validate: upload with + without a store header; confirm default-store fallback; confirm cross-store isolation (store A can't see store B's docs).
-
lib/cp-proxy.ts:getActiveStore()+storeIdoption; injectX-Vectorless-Store. -
StoreProvider(parallel to SessionProvider) + localStorage persistence of last-selected store. -
StoreSwitcherdropdown in the sidebar header. -
/dashboard/storeslist + create + delete page; CP-proxy route(s) for store CRUD. - Scope existing views to the active store: documents list, upload, playground, analytics.
- TS/Go/Python:
storeIdclient config;createStore/listStores/getStore/deleteStore; per-callstoreIdoverride on ingest / query / list; transport sends the header (precedence: per-call > config > none). - MCP:
mcp_storetable +store_idonmcp_document;vectorless_create_store/_list_stores/_get_store/_delete_storetools; ingest/list/query accept + validatestore_id. - Docs: refresh SDKS.md + MCP.md examples with stores.
- Profile on store: store declares its profile; CP passes
X-Vectorless-Profile; ingest applies it. (Depends on the Profiles Phase 1 scaffold.) - Store-bound API keys: key creation UI + enforcement; store-bound key needs no header.
- Per-store usage: surface usage rollups by store in the dashboard analytics.
- Cross-store query (default: no).
- Move documents between stores (lean: immutable v1).
- Slug vs id in dashboard URLs.
- When per-store quota enforcement (vs. org-level) kicks in.
- ../STORES.md — the design doc.
- ../PROFILES.md — domains; carried on the store.
- ../../ROADMAP.md — root checkbox document.