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:
Scott Idem
2026-04-28 16:10:17 -04:00
parent b4f0ca3e64
commit de07fa0e0e
3 changed files with 93 additions and 16 deletions

View File

@@ -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.