Layers and Merge Rules
Three layers, resolved in order.
| Layer | What | Rule |
|---|---|---|
| 1. base | base.sbx | always loaded, always first, never overridden |
| 2. selected | fragments from selects_on, --profile, requires | add freely; order does not matter |
| 3. workspace | the .wardcell.sbx in the working directory | may add, but never weaken base.sbx |
Layer 2 is order-independent by design. Fragments are parsed into one IR and merged by an explicit algorithm — nothing is concatenated, so there is no “last one wins” to reason about.
Filesystem: longest path wins
Section titled “Filesystem: longest path wins”Two rules on overlapping paths resolve by specificity — the more path components, the more specific — not by declaration order.
base: deny_read ["~"]fragment: allow_read ["~/projects/app"]→ ~/projects/app is readable; the rest of ~ is notThree ways this errors instead:
| Error | When |
|---|---|
BaseOverride | a layer-2/3 fragment tries to loosen a base-layer deny at equal-or-lesser specificity |
SameLayerTie | two layer-2 fragments assert allow and deny on the same path at equal specificity |
WorkspaceOverridesBase | a .wardcell.sbx grants something inside a path base.sbx denies |
A SameLayerTie is an authoring ambiguity, so it is rejected rather than
silently resolved. Fix it by making one rule more specific.
Longest-path-wins is what lets a shared fragment carve ~/.ssh/known_hosts out
of a base deny_read ["~/.ssh"]. The workspace layer does not get that: an
allow in a .wardcell.sbx on or inside any path base.sbx denies is refused,
however specific.
The workspace layer
Section titled “The workspace layer”A workspace policy may add fs, network and environment grants like any
fragment, except inside a base.sbx deny, and never native rules, which
render after base.sbx and could undo any of it. Network and environment need
no extra check: a deny wins over every allow whichever layer wrote it. An
organisation can turn the layer off in
config.toml.
See Workspace Policies.
Two grants that cannot coexist
Section titled “Two grants that cannot coexist”Naming one destination both as an allow_http block (inspected) and in
allow_tcp (spliced opaquely) is an error. The two are not additive — one
would silently win, and the narrower would look enforced while doing nothing.
The test is whether they share any destination, not whether they were spelled the same:
allow_http "api.example.com" # ports 80 and 443allow_tcp ["api.example.com:443"] # ← error: overlaps on 443
allow_http "*" # every host, 80 and 443allow_tcp ["db.internal:5432"] # ← fine: ports cannot intersectPractically
Section titled “Practically”- Adding reach? Write a new fragment. Grants union.
- Removing reach? Add a
deny_*. It wins from anywhere. - Hit a tie? Make one rule more specific.
.wardcell.sbxrejected? It is reopening somethingbase.sbxdenies. Put the grant in a fragment in the policy root, if it belongs anywhere.