# Change the guard profile

The hosted shell boots in `strict`. In this draft a user can switch the whole shell to `balanced` or `relaxed`; the choice applies to every window and is not remembered, so every boot starts in `strict` again. Loosening one applet at a time, remembered, is what [D-003](https://kernl.run/docs/decisions.md#d-003) asks for and is planned ([Roadmap](https://kernl.run/docs/roadmap.md#_6-guard)). An embedding host may choose the starting profile with `createKernl({ host, guard: { profile } })`, which changes limits only; the sandbox, the CSP, the frame lock and consent are fixed ([What a profile cannot change](#what-a-profile-cannot-change)).

## In the shell

1. Open the guard panel from the desktop bar (*Guard · strict · N*).
2. Under *Profile*, pick `balanced` or `relaxed`. The list of mitigations updates to show the new limit for each one. The CSP does not change; it never does.
3. The change calls `kernl.guard.setProfile(profile)`. kernl's own counting layers switch to the new limits at once: the ingress guard, visibility gate and focus firewall are rebuilt from the new profile, their counters restart and the window-level messages they held are dropped. kehto's runtime firewall follows too: kernl reinstalls the new rates for every open window and sets the profile's unfocused multiplier and startup burst ([D-020](https://kernl.run/docs/decisions.md#d-020)). Requests a hidden window holds stay held. The choice is not persisted: a reload starts in `strict` again.

A quarantined applet stays closed whatever the profile: its binding was revoked and its frame removed when it was quarantined. Its window's grants panel offers **Release from quarantine**, which clears kernl's quarantine record for that window (`kernl.guard.release(windowId)`) and the panel's marker; it does not bring the frame back. To run the applet again, open it again: a new window starts with a fresh budget.

The per-applet, remembered override that [D-003](https://kernl.run/docs/decisions.md#d-003) describes is listed under [Alpha status](https://kernl.run/docs/alpha-status.md#what-does-not-work-yet).

## In code

A profile is a `GuardConfig` from `createGuardConfig(profile)`. The three guard layers each take that config in an options object, together with an optional clock and an event sink:

```ts
import {
  createFocusFirewall,
  createGuardConfig,
  createIngressGuard,
  createVisibilityGate,
  type GuardEvent,
  type GuardProfile,
} from '@kernlize/guard';

function guardFor(profile: GuardProfile, onEvent: (event: GuardEvent) => void) {
  const config = createGuardConfig(profile);
  return {
    config,
    ingress: createIngressGuard({ config, onEvent }),
    gate: createVisibilityGate({ config, onEvent }),
    firewall: createFocusFirewall({ config, onEvent }),
  };
}
```

`now` defaults to `Date.now` in all three; inject it in tests. Switching a mounted window from one profile to another means, at the guard level:

1. Build the three layers from the new config. The layers hold no reference to the profile, so there is no in-place update; the old `IngressGuard`, `VisibilityGate` and `FocusFirewall` are replaced.
2. Drain what the old gate and old ingress guard still hold (`drain(windowId)`) and admit it through the new layers, so nothing queued is lost.
3. Call `release(windowId)` on the old ingress guard, or simply drop it: quarantine lives in the guard instance and does not survive the swap.
4. If the shell also runs kehto's runtime firewall, hand it the new limits: `setUnfocusedMultiplier` and `setBurstGuard` from `config.firewall`, and the per-applet rates, which are not in a fresh config; `applyNappletRules(config, dTag)` adds them, and the focus firewall does this itself on first observation.

`@kernlize/core` does this inside `createGuardPipeline`: `GuardFacade.setProfile(profile)` rebuilds the three layers from `createGuardConfig(profile)`, clears what they held, re-applies the known visibility of every window and emits on `profile$`; `GuardFacade.release(windowId)` clears a quarantine record on its own, which does not bring back a frame that was already removed. `createKernl` follows `profile$`: `applyRuntimeFirewallProfile` sets the runtime's unfocused multiplier and startup burst, and `applyRuntimeFirewallRules` reinstalls the rates of every open window. Applets mounted later get the rates at mount. The [core page](https://kernl.run/docs/packages/core.md#the-kernl-surface) documents that surface.

## What a profile cannot change

- The Content Security Policy. `createAppletCsp(profile)` returns the same policy for every profile; see [why](https://kernl.run/docs/concepts/guard.md#why-csp-never-loosens) and `csp.feature`, scenario "the relaxed profile does not loosen the CSP".
- The sandbox, the frame lock and consent. `planMount` passes the profile only to `createAppletCsp`; `sandbox="allow-scripts"`, the nested-frame lock and the destructive-signing gate never read it.
- Whether a mitigation is enabled. Every profile enables all eleven; `describeMitigations(config)` only changes the `limit` text.
- Grants. A profile is about rate, not authority. [Grant and revoke](https://kernl.run/docs/how-tos/grant-and-revoke.md) covers authority.

## What a profile changes

Exact values for every limit and every per-class rate are in [Guard](https://kernl.run/docs/concepts/guard.md#profiles). In one line each: `balanced` raises the headline limits by 1.5× to 2× (`ingressBurst` 8 → 12, `ingressQueue` 64 → 128, `quarantineThreshold` 300 → 420, `hiddenResourceAllowance` 8 → 16, `deferQueue` 200 → 400, envelope caps 64/256 KiB → 128/512 KiB, `unfocusedMultiplier` 0.25 → 0.5) and doubles every rate; `relaxed` goes to 24, 192, 800, 32, 600, 192/768 KiB and 0.75, and triples every rate.

## Adding a profile

Do not. Three is the number the UI was designed around, and a fourth profile would be a developer deciding how much an applet may do, which `SOUL.md` reserves for the user. If an applet needs a limit no profile provides, that is a reason to revisit the limits in `strict`, with a scenario in `packages/guard/features/profiles.feature` that shows the applet a competent developer could not otherwise build.
