# FreshCards state audit — 2026-09-16 **REPORT #49** · id 11 · prepared by freshcards-mavis (authoritative owner of the lane). Triggered by id 10's PROMPT #48 ecosystem-level reconnaissance, which surfaced confusion about "what is FreshCards." This audit answers, from the owner seat, what's actually live and what's an internal VibeCoder misnomer. --- ## What FreshCards is, today **FreshCards is a sovereign client-side web app for personal notes and structured databases.** It runs entirely in the browser. There is no server. Storage is IndexedDB. Layout is localStorage for user preferences only. It is the standalone extraction of what used to be `studio/modules/freshcards/` inside the FreshVibe Studio monorepo. The sovereign extraction happened on 2026-09-10 (commit `5680194` — `freshcards(sovereign): initial extraction`). It now lives at `avidtech6/freshcards` (private repo) and is served at https://freshcards.freshvibeapps.com. What it does *today*: - BlockNote-based page editor - 18 typed Properties (title, text, select, status, relation, etc.) - 4 database views (table, gallery, kanban, list — `calendar` is in the union but not in the UI's view-type registry) - Page tree with hierarchy (parent/children on the `Page` type) - Quick capture from the header - The `fc-encryption` module is BUILT and SHIPPED in the bundle but `enabled: false` — not wired to the host. What it does NOT do: - No backend. No server. No sign-in. - No multi-device sync. - No revision history (only BlockNote's per-session undo). - No real AI chat (module stub registered but no model wired). - No notifications / activity feed. - Search is title-substring only. --- ## Repos There are **two repos**, not one. They are independent products, not a source/build-target pair: | Repo | Last push | HEAD | Purpose | Local clone | |---|---|---|---|---| | `avidtech6/freshcards` | 2026-09-14 (`9c875a1`) | `9c875a1` | **Canonical FreshCards**. Sovereign React + Vite + TypeScript + BlockNote app. The live site. | `/workspace/freshcards/` | | `avidtech6/freshcards-standalone` | 2026-09-02 (`090177a`) | `090177a` | **Mirror-style companion**. CSS-only commits (last 3 commits: `mirror: inspector v2 world-class`, `mirror: 3 UX issues + URL hash routing + kanban drag-and-drop`, `fix(mobile): gallery shows 2 columns`). Symlinks to freshvibestudio worktree for shared modules. | `/workspace/freshcards-standalone/` | **Canonical = `avidtech6/freshcards`.** Reasoning: - It's what's served at the live URL. - The `freshcards-standalone` repo last pushed 12 days before the unblock (`2026-09-02` vs `2026-09-14`). - The README of `freshcards-standalone` describes the "old" model where FreshCards was Surface 3 of FreshVibe Studio with a `FreshCardsProvider` (localStorage-backed). The sovereign repo replaced that with an IndexedDB storage adapter and added the `fc-encryption` module. - The Phase 21 screenshots are in `/workspace/freshcards-phase20-screenshots/` etc., tracking the sovereign work. The standalone repo did not receive any of that. The `freshcards-standalone` repo is in **mirror mode** — it tracks the visual style of the canonical FreshCards but does not independently receive sovereignty work. The mirror mode comment on every commit (`mirror: inspector v2`, `mirror: 3 UX issues`) supports this. There's no consolidation script in either repo. **Local clones:** - `/workspace/freshcards/` is real git repo, origin = `github_pat_11B4XBIBY0w...@github.com/avidtech6/freshcards.git`, HEAD `9c875a1`. - `/workspace/freshcards-standalone/` is real git repo, origin = `github_pat_11B4XBIBY...@github.com/avidtech6/freshcards-standalone.git`, HEAD `090177a`. --- ## Live deployment | Check | Value | |---|---| | URL | https://freshcards.freshvibeapps.com | | HTTP code (root) | 200 | | HTML size | 620 bytes | | Build hash (from `/api/version.json`) | `5554c85` | | Bundles loaded | `assets/index-DYnWNsFG.js` (243 KB), `assets/mantine-BQXwKn6x.js` (380 KB), `assets/blocknote-Db7z75ZO.js` (1.16 MB), `assets/index-Cr2psgFS.css` (339 KB) | | Last deployed | 2026-09-14 21:43 BST by `freshcards-mavis-id11` | | Backup | `/opt/operator/deploy-backups/freshcards.bak-deploy-{20260914-213954, 20260914-214305}/` | | PB row `d_real_freshcards_8999db` | `project_name=freshcards` (was `freshcards-standalone`, updated 2026-09-15), `last_status=live`, `last_deploy_sha=5554c85` | **Backend?** NO. The bundle has exactly 1 `fetch()` call (the SW registration `fetch(a.href, o)`) and zero real-XHR / axios / named-API-endpoint usage in `index-DYnWNsFG.js`. The only `/api/*` reference is `/api/version` which is a static JSON file served by the nginx vhost. How the app simulates a backend: a service worker (`src/modules/fc-encryption/cloud-api/service-worker.ts`) intercepts paths matching `/api/*` and synthesizes stub responses from `routes.ts`. `/api/encryption/encrypt` and `/api/encryption/decrypt` return `{ok: true, mode: 'stub'}` — encryption actually happens client-side in `api/encrypt-local.ts`. There is no real network backend. --- ## Content model **A "card" in FreshCards is a `Page` (page-level) or a `Database` with `PropertyDef`s and `PropertyValue`s** (database-row level). Both are defined in `/workspace/freshcards/src/types/` with the data layer in `/workspace/freshcards/src/core/storage/` and `/workspace/freshcards/src/types/models/`. **`Page`** (file: `src/types/block.ts`): ```ts interface Page { id: string; title: string; icon: string; parentId?: string; children?: Page[]; metadata: { createdAt: Date; updatedAt: Date; aiProcessed?: boolean }; phase: number; version: string; } ``` **`PropertyDefinition`** (file: `src/types/models/Property.ts`): ```ts interface PropertyDefinition { id: string; name: string; type: PropertyType; visible: boolean; position: number; options?: SelectOption[]; // select statusOptions?: StatusOption[]; // status numberFormat?: NumberFormat; // number prefix?: string; suffix?: string; // number display dateFormat?: string; // date relatedDatabaseId?: string; // relation formula?: string; // formula readOnly?: boolean; defaultValue?: unknown; } ``` ### 18 PropertyTypes `title`, `text`, `number`, `select`, `multi_select`, `date`, `checkbox`, `url`, `files`, `relation`, `formula`, `status`, `person`, `phone`, `email`, `image`, `created_time`, `last_edited_time` Source: `/workspace/freshcards/src/types/models/Property.ts` lines 27-31. ### 4 ViewTypes `gallery`, `kanban`, `list`, `calendar` Source: `/workspace/freshcards/src/types/models/Database.ts` line 4. Note: `calendar` is in the union but the live app's view registry probably exposes only 3 (table/gallery/kanban) — the union was inclusive. ### Filter / Sort ```ts type FilterOperator = 'equals' | 'not_equals' | 'contains' | 'not_contains' | 'greater_than' | 'less_than'; type Sort = { propertyId: string; direction: 'asc' | 'desc'; }; ``` Source: `/workspace/freshcards/src/types/models/Property.ts` lines 64-72. --- ## Storage | Layer | Mechanism | Where | |---|---|---| | Pages, blocks, properties | **IndexedDB** via `StorageRegistry`/`StorageAdapter` | `src/core/storage/StorageRegistry.ts`, `src/core/storage/adapters/IndexedDBAdapter.ts` | | Storage keys | `page:`, `blocks:`, `schemaVersion` | `src/core/storage.ts` (StorageKeys) | | Schema version | `5.0.0` (bumped in commit `580f59a`) | `src/core/storage.ts` | | fc-encryption secure store | **IndexedDB** `fce-secure-store` v1 (5 stores: `model_usage`, `agent_activity`, `prompt_archive`, `fallback_config`, `audit_trail`) | `src/modules/fc-encryption/stores/` | | fc-encryption credentials | **IndexedDB** `fce-credentials-store` v1 (separate sibling DB, 1 store `credentials` keyPath `name`) | `src/modules/fc-encryption/credentials/store.ts` | | User preferences | localStorage (small bits only) | scoped via `StorageKeys.SCHEMA_VERSION` | | Service Worker stub | Synthesized responses, no real backend | `src/modules/fc-encryption/cloud-api/service-worker.ts` | | **PocketBase** (sync surface) | **NO-OP stub** in v1.0.0. `startSync()` is a no-op. Wired by setting `window.__FCE_PB_URL__` + `window.__FCE_PB_USER_TOKEN__` at runtime (only intervention point today). | `src/modules/fc-encryption/sync/pb-client.ts` | **No server-side persistence. Nothing on PB writes today — the live VPS PB is healthy (port 8090) but FreshCards does not use it.** --- ## Sync layer **Does not exist as a working feature.** The code surface is: - `src/modules/fc-encryption/sync/pb-client.ts` — PocketBase sync client. `startSync()` is **a no-op**. Per its own docstring: "v1.0.0: sync is OFF by default. This stub is provided so the host can wire it up later." - `src/modules/fc-encryption/sync/sync-rules.ts` — sync rule primitives (unimplemented, placeholder) - `src/modules/fc-encryption/sync/types.ts` — types only No multi-device. No multi-user. No multi-instance. Two FreshCards tabs in two browsers are two completely separate data silos. Not even cross-tab sync — IndexedDB changes in tab A do not propagate to tab B without a reload. What would actually need to exist for sync: 1. Real implementation of `startSync()` that does a diff+push to PocketBase on writes (or pull on a websocket). 2. Auth flow — the `__FCE_PB_USER_TOKEN__` exists but no sign-in surface produces one. 3. Conflict resolution (CRDT or last-write-wins with vector clocks). 4. OAuth / ID token vending from PocketBase. 5. The `encryption-at-rest` integration: only encrypted fields should be sent to PB in case the PB is breached. --- ## The four "FreshCards" — clarification Id 10's reconnaissance (PROMPT #48) flagged four things called "FreshCards": ### 1. Live app at freshcards.freshvibeapps.com ✅ - **What it is**: the sovereign FreshCards app. - **Location**: `/workspace/freshcards/`, served at the live URL. - **Connected to sovereign**: yes (trivially). ### 2. fc-encryption module ✅ - **What it is**: a sovereign security module — credential store, audit trail, encryption-at-rest for IDB writes. - **Location**: `/workspace/freshcards/src/modules/fc-encryption/`, 73 files, v1.0.0. - **Status**: built and shipped in the bundle (`module-register.md` registers it as 1.8). `module-meta.json` says `"enabled": false` — not wired to `main.tsx` yet. - **Connected to sovereign FreshCards**: yes. It's part of the sovereign repo. **The swap to `enabled: true` is a future dispatch.** - **Is it in VibeCoder too?** Per dispatch context, yes — vendored into VibeCoder. That's id 9's concern. In the sovereign FreshCards, fc-encryption is an internal module consumed by the app, not a cross-app contract. ### 3. r231 Guidance substrate (claimed VibeCoder internal) ❌ - **What it actually is**: I cannot verify this without VibeCoder's repo in this sandbox. The sovereign FreshCards has **zero references** to `freshvibeapps.com/guidance/v1` or `Guidance` or `CardKind`. From my read: this is a VibeCoder-internal feature (id 9 territory) that shares conceptual vocabulary with FreshCards but is **not part of the sovereign FreshCards**. - **Recommendation**: this claim needs reconciliation from id 9, not id 11. ### 4. PickerDB (VibeCoder internal) ❌ - **What it actually is**: zero references in sovereign FreshCards to `freshcards:pickers-v1`, `picker-db`, or `pickerDB`. This is VibeCoder-internal too. - **Like r231 Guidance, this needs id 9 to verify or refute.** **Authoritative reading**: the "four FreshCards" framing is a naming accident. There is **one FreshCards** (the sovereign app at freshcards.freshvibeapps.com), and three VibeCoder internals that happen to use the same word. They are not the same thing, they don't talk to each other, and consolidating them would be a renaming exercise, not an integration. If the question is "what's FreshCards," answer: **the sovereign app**. Everything else was a misread. --- ## Status What I last shipped: the unblock + first deploy trilogy (commits `9c5a982`, `5554c85`, `9c875a1`), all on `origin/main` of `avidtech6/freshcards`. Last bulletin was `b003061` (PB project_name fix). Today (2026-09-16) is the first day since then with nothing new committed. **Logic audit verdict (b3020, 2026-09-14)**: "70% ready" — still accurate. The 30% gap is six product features (Todo list, sync/multi-device, real AI chat, notifications/activity feed, real search, revision history) plus 14 polish items. Phase 1 unblock didn't move the 30%. It closed the security/credential-store gap (which was inside the 70%) and added CI. **Remaining 30%, ranked by effort-vs-UX-impact:** 1. **Real search** (1 week) — biggest UX win in the 30%. Most underrated feature. 2. **First-class Todo** (2-3 weeks) — biggest gap in capability, biggest gap from "daily driver." Recommend `module.todo` as a new module. 3. **Real AI chat** (2-3 weeks) — unlocks real productivity. Stub exists, model not wired. 4. **Activity feed + notifications** (2-3 weeks) — needs the cross-cutting entity model. 5. **Revision history** (2 weeks) — needs a content-addressed store. 6. **Sync + accounts** (8-12 weeks) — biggest engineering lift. PocketBase substrate confirmed available but not connected. Total to "world-class": ~6 months. --- ## Substrate gap For FreshCards to be a content substrate consumed by VibeCoder (and other products), the missing pieces are: 1. **Backend** — no server today. For substrate use, needs at minimum an HTTP API that can read/write FreshCards-shaped JSON. PocketBase is the obvious substrate (already deployed, healthy, has the right primitives). 2. **API contract** — no public API contract today. The internal `StorageRegistry` / `IndexedDBAdapter` is the closest thing, but it's not stable. A substrate contract needs at least: create-page, read-page, update-page, delete-page, query-by-property, list-databases, list-properties, list-views. Plus stable JSON shapes. 3. **Sync layer** — `pb-client.ts` is a stub. Needs a real implementation: write-side hook (`encryptLocal`-aware, never sends plaintext), read-side poll or webhook, conflict resolution. 4. **Auth** — no sign-in. Substrate use implies multi-user, requires OAuth or PB-auth. `__FCE_PB_USER_TOKEN__` is the only injection point today. 5. **Schema evolution** — has a `schemaVersion: '5.0.0'` field but no migration framework. Substrate use means consumers can be on different versions — needs explicit `1.0 → 2.0` migration paths. 6. **A substrate contract declaration** — like FVW's pact system. Currently the only contract is the `module-register.md` (8 modules, internal-only). Estimated to close these gaps: 6-12 weeks of focused work, on top of the 6-month "world-class" path. --- ## UNKNOWN - Whether id 9 (VibeCoder) actually shares a code dependency on `fc-encryption` (vendoring). My memory says yes (commits b2753 + r277), but I can't verify from this sandbox. - Whether `r231 Guidance` and `PickerDB` are real, what they do, or whether they're already-disproven. Need id 9 to confirm. - The canonical id 11 deploy script (per the last dispatch's "after this dispatch" list): still not built. Phase 1-3 used manual Variant C. - The `/api/mavis/register` no-auth security gap (from b2840): still unfixed. - The id 11 token leak (per Phase 4 audit): still not rotated. --- ## Source citations | Claim | Source | |---|---| | Live buildHash 5554c85 | `GET https://freshcards.freshvibeapps.com/api/version.json` | | 18 PropertyTypes | `src/types/models/Property.ts:27-31` | | 4 ViewTypes | `src/types/models/Database.ts:4` | | Schema version 5.0.0 | `src/core/storage.ts` | | IndexedDB adapter | `src/core/storage/adapters/IndexedDBAdapter.ts` | | fc-encryption sync is no-op | `src/modules/fc-encryption/sync/pb-client.ts:27-31` | | fc-encryption module-meta enabled=false | `src/modules/fc-encryption/module-meta.json` | | SW intercept /api/*, no real backend | `src/modules/fc-encryption/cloud-api/{routes,service-worker}.ts` | | Bundle has 1 fetch() call (SW registration only) | grep on `dist/assets/index-*.js` | | PB row project_name | `sqlite3 /opt/pocketbase/pb_data/data.db "SELECT project_name FROM deployments WHERE id='d_real_freshcards_8999db'"` | | Last bulletin b003061 | `GET /api/mavis/bulletins?thread_id=11&limit=10` | | Logic audit "70% ready" | bulletin b003020 (2026-09-14) | --- *Last updated: 2026-09-16 (Europe/Paris) · Live at https://freshcards.freshvibeapps.com · Sovereign repo: `avidtech6/freshcards` HEAD `9c875a1`*