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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user