docs: capture IDAA IDB audit results and layout security model
- TODO__Agents.md: mark IDAA IDB caching item complete (audited 2026-04-28); all protection layers confirmed in place, no code changes needed - GUIDE__SvelteKit2_Svelte5_DexieJS.md: add "SvelteKit Layout Hierarchy: Security and Execution Order" section explaining execution order, auth-gate consequences, pre-gate risks in +page.ts/+layout.ts, and the reactivity-guard vs auth-guard distinction for IDAA $effect blocks - BOOTSTRAP__AI_Agent_Quickstart.md: add Mistake #7 — treating $effect blocks as auth bypass risks vs understanding the real layout hierarchy Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -253,6 +253,16 @@ These are real incidents — know them before you start.
|
||||
6. **Deleting files with `rm`** — always move to `~/tmp/agents_trash`. A deleted file may
|
||||
contain context that's not recoverable from git if it was gitignored.
|
||||
|
||||
7. **Treating `$effect` blocks as auth bypass risks** — a `$effect` inside a child
|
||||
component cannot bypass a parent `+layout.svelte` auth gate. Children only mount if
|
||||
the parent calls `{@render children?.()}`. Adding redundant auth guards to `$effect`
|
||||
blocks that can only run after the parent gate already passed is unnecessary — and
|
||||
misleads future readers into thinking the parent gate is not sufficient on its own.
|
||||
The **real** pre-gate risk is `+page.ts` / `+layout.ts`: universal load functions run
|
||||
before any layout mounts and also fire during SvelteKit link prefetch. Keep those files
|
||||
clean of data loads in private modules. See `GUIDE__SvelteKit2_Svelte5_DexieJS.md` →
|
||||
"SvelteKit Layout Hierarchy: Security and Execution Order" for the full explanation.
|
||||
|
||||
---
|
||||
|
||||
## 8. Source Layout (Quick Reference)
|
||||
|
||||
@@ -233,6 +233,79 @@ export function createLiveQueryStore<T>(query: () => T | Promise<T>) {
|
||||
|
||||
The `createLiveQueryStore` function creates a readable store that automatically updates whenever the data in the `friends` table changes. The `$friends` variable in the component will always contain the latest data from the database.
|
||||
|
||||
## SvelteKit Layout Hierarchy: Security and Execution Order
|
||||
|
||||
Understanding *when* SvelteKit code runs is critical for private-data modules like IDAA.
|
||||
|
||||
### Execution order on any navigation
|
||||
|
||||
```text
|
||||
1. +layout.ts / +page.ts ← run FIRST — before any component mounts
|
||||
also fired by SvelteKit link prefetch (on hover)
|
||||
2. Parent +layout.svelte mounts → its $effect blocks run
|
||||
3. Child +layout.svelte mounts → only if parent called {#render children?.()}
|
||||
4. +page.svelte mounts → only if every parent in the chain rendered children
|
||||
5. $effect blocks in all of the above run after mount
|
||||
```
|
||||
|
||||
### The auth-gate consequence
|
||||
|
||||
A `{:else if authenticated} {@render children?.()}` block in a `+layout.svelte`
|
||||
controls whether **everything below it** ever mounts. If the gate blocks rendering,
|
||||
no child layout or page component instantiates — their `$effect` blocks, event
|
||||
handlers, and liveQuery closures never run.
|
||||
|
||||
```svelte
|
||||
<!-- (idaa)/+layout.svelte -->
|
||||
{:else if $ae_loc.trusted_access || $idaa_loc.novi_verified}
|
||||
{@render children?.()} ← children only mount if this branch runs
|
||||
{:else}
|
||||
<p>Access Denied</p> ← children never mount; their $effects never run
|
||||
{/if}
|
||||
```
|
||||
|
||||
**`$effect` blocks inside a child component cannot bypass a parent layout auth gate.**
|
||||
They are already inside the gate. Adding redundant auth guards to `$effect` blocks
|
||||
that only run after a parent has already verified access is unnecessary — and misleads
|
||||
future readers into thinking the parent gate alone is not sufficient.
|
||||
|
||||
### Where the actual pre-gate risk lives: `+page.ts` / `+layout.ts`
|
||||
|
||||
Universal load functions run *before* components mount and *before* layout effects
|
||||
execute. They also fire during SvelteKit link prefetch — triggered by the user
|
||||
hovering a link, even if they never navigate. This makes them unsafe for private data:
|
||||
|
||||
```text
|
||||
User hovers an /idaa/ link →
|
||||
SvelteKit prefetch fires →
|
||||
+page.ts runs (no layout has mounted yet, no auth gate has run) →
|
||||
API call / IDB write happens for an unauthenticated user
|
||||
```
|
||||
|
||||
**Rule for private modules (IDAA, Journals):** `+page.ts` and `+layout.ts` files must
|
||||
not call any data load functions that write to IDB. Move all data loading to `$effect`
|
||||
blocks in the corresponding `+page.svelte`, gated inside the auth-checked layout render.
|
||||
The comments in every `+page.ts` under `src/routes/idaa/(idaa)/` explain this pattern.
|
||||
|
||||
### The `$effect` auth guards in IDAA `+page.svelte` files
|
||||
|
||||
These ARE still useful — but for a different reason than layout bypass:
|
||||
|
||||
```ts
|
||||
// In bb/+page.svelte
|
||||
$effect(() => {
|
||||
if (!$idaa_loc.novi_verified && !$ae_loc.trusted_access) return;
|
||||
posts_func.load_ae_obj_li__post(...)
|
||||
});
|
||||
```
|
||||
|
||||
Because `$ae_loc` is a Svelte 4 coarse-grained store, any unrelated write to it
|
||||
(iframe height, SWR reload) re-triggers this `$effect`. The guard prevents a spurious
|
||||
API call if `$idaa_loc.novi_verified` has been cleared between re-runs (e.g. TTL
|
||||
expiry mid-session). It is a reactivity guard, not a layout-bypass guard.
|
||||
|
||||
---
|
||||
|
||||
## Page Load Strategies (Avoiding the "Waterfall")
|
||||
|
||||
When loading data for a primary page view (e.g., viewing a specific Journal, Session, or Person), you must choose a synchronization strategy to ensure the UI renders correctly on the first load.
|
||||
|
||||
@@ -118,22 +118,16 @@ suddenly jumps to 0 errors, verify it's not because a bad `.d.ts` replaced a pac
|
||||
Run `npx svelte-check 2>&1 | grep ModalProps` to get the current list.
|
||||
Fix pattern: replace `children` prop binding with Svelte snippet syntax per flowbite-svelte docs.
|
||||
|
||||
- [ ] **[IDAA] Do not cache IDAA data in IDB when access is denied (2026-04-19)**
|
||||
If a user is not authenticated or receives an access-denied response, the frontend must
|
||||
**not** pre-fetch or cache any IDAA content (Posts, Archives, Events/Recovery Meetings)
|
||||
into the local browser IndexedDB (Dexie). Storing private IDAA data in IDB on an
|
||||
unauthenticated device is a privacy violation — the data would persist in the browser
|
||||
even after the session ends, accessible to the next person who opens DevTools.
|
||||
|
||||
**Fix pattern:**
|
||||
- All IDAA SWR load functions (`load_ae_obj_li__*` in `ae_idaa/`, `ae_posts/`, and the
|
||||
IDAA-specific event queries) must gate on a successful auth check before calling
|
||||
`_refresh_*_background` or writing to IDB.
|
||||
- If the API returns a 401/403, do not write to Dexie — return/throw early.
|
||||
- On explicit logout or Novi auth invalidation, purge IDAA tables from IDB
|
||||
(`db_idaa`, `db_posts`, and any IDAA event records in `db_events`).
|
||||
- Audit all `+page.ts` / `+layout.ts` files under `src/routes/idaa/` to confirm
|
||||
no eager prefetch runs before the auth guard resolves.
|
||||
- [x] **[IDAA] Do not cache IDAA data in IDB when access is denied (2026-04-19, audited 2026-04-28)**
|
||||
Full audit confirmed all protection layers are in place. No code changes required.
|
||||
- All `+page.ts` / `+layout.ts` under `src/routes/idaa/` are clean — no SWR loads run before auth resolves.
|
||||
- All `$effect` SWR calls in IDAA `+page.svelte` files are gated on `$idaa_loc.novi_verified || $ae_loc.trusted_access`.
|
||||
- `(idaa)/+layout.svelte` purges `db_posts`, `db_archives`, `db_events` on auth failure, no-UUID/no-session, and inconsistent state.
|
||||
- `sign_out()` calls `indexedDB.deleteDatabase()` on all IDAA databases.
|
||||
- API 401/403 responses fail-fast in `api_get_object.ts` (throw before any IDB write).
|
||||
- `idaa_trig` is in-memory `writable()` only — cannot carry stale trigger state across sessions.
|
||||
- `$effect` auth guards in IDAA page components are reactivity guards (prevent spurious SWR calls on coarse `$ae_loc` writes), NOT auth-bypass guards. SvelteKit layout hierarchy already prevents child components from mounting when `(idaa)/+layout.svelte` blocks rendering.
|
||||
- Doc: SvelteKit layout hierarchy security model captured in `GUIDE__SvelteKit2_Svelte5_DexieJS.md` and `BOOTSTRAP__AI_Agent_Quickstart.md` (Mistake #7).
|
||||
|
||||
- [ ] **[IDAA] Make `contact_li_json_ext` searchable — Recovery Meeting contact search (2026-04-08)**
|
||||
Members cannot search for meetings by contact name or email. `contact_li_json` data is not
|
||||
|
||||
Reference in New Issue
Block a user