Somewhere safe to be wrong.
A sandbox is a scratch context of one browser session's own, hung below
CxWell so it sees what CxWell sees (every shipped context but the opt-in theories,
the kb/middle/ files that state no genlCx edge from CxWell) and nothing shipped
sees it. A
reader can therefore use every type, every relation and every rule the KB ships, and
cannot damage any of them: their content is visible only from inside, and one control
takes all of it away again.
Why that shape and not a permission system: visibility here is logical, not
administrative. genlCx already decides what a context can see, and hanging the
sandbox at the bottom of the spindle gives exactly the asymmetry wanted — everything
flows in, nothing flows out — with no new concept and nothing to enforce. A shipped
rule firing over sandbox facts places its conclusion in the sandbox, because
placement is the maximal common descendant of the rule's context and the antecedents'
(docs/contexts.md), and the sandbox is the only context below both. So the derived
content is inside the thing that gets discarded, without anything arranging for that.
Four facts about the lifecycle:
genlCx edge per idle visitor./find, CxWell's term page, /op :contexts).max-sandboxes per KB. Opening one more resets the
lowest-priority sandbox on the roster below (docs/web.md).edit's :remove, and the genlCx edge with them. The edge is not in the
extent — genlCx is a forced-decontextualized predicate, so it is stored in
CxUniverse — which is why it is fetched by hand rather than swept up with the
rest.Promotion — moving something out of a sandbox into a context that outlives it — is deliberately not here. A sandbox is a dead end, and a dead end that cannot be half-escaped is easier to reason about than one with an entry point in it.
Somewhere safe to be wrong. A **sandbox** is a scratch context of one browser session's own, hung below `CxWell` so it sees what CxWell sees (every shipped context but the opt-in theories, the `kb/middle/` files that state no `genlCx` edge from CxWell) and nothing shipped sees it. A reader can therefore use every type, every relation and every rule the KB ships, and cannot damage any of them: their content is visible only from inside, and one control takes all of it away again. Why that shape and not a permission system: visibility here is *logical*, not administrative. `genlCx` already decides what a context can see, and hanging the sandbox at the bottom of the spindle gives exactly the asymmetry wanted — everything flows in, nothing flows out — with no new concept and nothing to enforce. A shipped rule firing over sandbox facts places its conclusion **in the sandbox**, because placement is the maximal common descendant of the rule's context and the antecedents' (docs/contexts.md), and the sandbox is the only context below both. So the derived content is inside the thing that gets discarded, without anything arranging for that. Four facts about the lifecycle: - **The context is created on the first write, not on the first page.** A reader who only looks costs the KB nothing, and a KB full of empty sandboxes would be a KB with a `genlCx` edge per idle visitor. - **The server mints the session token and tags it.** The cookie carries the token and an HMAC of it under a key fixed at start, and a cookie whose tag does not check is replaced with a fresh one, so a client cannot choose a sandbox. The context name carries a digest of the token rather than the token, because every reader sees every context's name (`/find`, `CxWell`'s term page, `/op :contexts`). - **A process keeps at most `max-sandboxes` per KB.** Opening one more resets the lowest-priority sandbox on the roster below (docs/web.md). - **Reset is a real teardown**, not a flag: every sentex in the extent goes through `edit`'s `:remove`, and the `genlCx` edge with them. The edge is not in the extent — `genlCx` is a forced-decontextualized predicate, so it is stored in `CxUniverse` — which is why it is fetched by hand rather than swept up with the rest. Promotion — moving something out of a sandbox into a context that outlives it — is deliberately not here. A sandbox is a dead end, and a dead end that cannot be half-escaped is easier to reason about than one with an entry point in it.
(add-cookie resp c)resp with one more Set-Cookie. Ring takes a header value as a string or as a
collection of them, so a second cookie extends the header rather than replacing
what is there.
Here rather than in vaelii.browser.web, which is the other caller: that namespace
requires this one, so a helper the two share can only live on this side of the edge.
The case is a response carrying both cookies the browser has — a reader who sets a
reading preference on the same request that mints their session token — where an
assoc drops whichever of the two ran first.
`resp` with one more `Set-Cookie`. Ring takes a header value as a string or as a collection of them, so a second cookie **extends** the header rather than replacing what is there. Here rather than in `vaelii.browser.web`, which is the other caller: that namespace requires this one, so a helper the two share can only live on this side of the edge. The case is a response carrying both cookies the browser has — a reader who sets a reading preference on the same request that mints their session token — where an `assoc` drops whichever of the two ran first.
(context-for token)The sandbox context named by token, or nil when the token is not well-formed.
CxSandbox<digest> satisfies the context naming invariant (Cx prefix,
CapitalCamelCase), so it is an ordinary context in every other respect. The digest
is the first 128 bits of the token's SHA-256: the name is listed to every reader, so
it must not carry the token a cookie holds.
The sandbox context named by `token`, or nil when the token is not well-formed. `CxSandbox<digest>` satisfies the context naming invariant (`Cx` prefix, CapitalCamelCase), so it is an ordinary context in every other respect. The digest is the first 128 bits of the token's SHA-256: the name is listed to every reader, so it must not carry the token a cookie holds.
(context-of req)The sandbox context this request belongs to, or nil when it carries no valid token.
The sandbox context this request belongs to, or nil when it carries no valid token.
(extent target ctx)Every sentex stored in the sandbox — what the reader put there and what the shipped rules concluded from it, which are in the same place by construction.
Every sentex stored in the sandbox — what the reader put there and what the shipped rules concluded from it, which are in the same place by construction.
(live? target ctx)Does this sandbox exist in the KB yet? It exists exactly when its edge does; the extent can be empty (everything in it retracted) and the sandbox still be open.
Does this sandbox exist in the KB yet? It exists exactly when its edge does; the extent can be empty (everything in it retracted) and the sandbox still be open.
(max-sandboxes)How many sandboxes a KB holds at most (VAELII_SANDBOX_MAX).
How many sandboxes a KB holds at most (`VAELII_SANDBOX_MAX`).
How many sentexes one sandbox may store. /assert refuses a form that would take its
sandbox past this, with :over-ceiling; the conclusions an admitted form derives may
pass it. Only the lines addressed to the sandbox count: a line the form writes into
another context counts toward no ceiling.
How many sentexes one sandbox may store. `/assert` refuses a form that would take its sandbox past this, with `:over-ceiling`; the conclusions an admitted form derives may pass it. Only the lines addressed to the sandbox count: a line the form writes into another context counts toward no ceiling.
(mint-token)A fresh session token: 128 bits from SecureRandom, as 32 hex digits, which need no
encoding in a cookie or a symbol.
A fresh session token: 128 bits from `SecureRandom`, as 32 hex digits, which need no encoding in a cookie or a symbol.
(note-use! target ctx)Record a request by the session whose sandbox is ctx.
Record a request by the session whose sandbox is `ctx`.
(note-write! target ctx)Record a write request by the session whose sandbox is ctx, and its size after.
Record a write request by the session whose sandbox is `ctx`, and its size after.
(open target ctx)Make sure ctx exists, and answer it. Idempotent — the edge is find-or-create, so a
second call on a live sandbox writes nothing. Opening a sandbox past max-sandboxes
resets the lowest-priority one first. Bare, not !: it removes only through reset!.
Make sure `ctx` exists, and answer it. Idempotent — the edge is find-or-create, so a second call on a live sandbox writes nothing. Opening a sandbox past `max-sandboxes` resets the lowest-priority one first. Bare, not `!`: it removes only through `reset!`.
(reset! target ctx)Discard the whole sandbox: every sentex in it, then the edge that made it a context, and its roster entry.
One edit, so it is one settle, and the dependency-directed sweep does the rest — a
conclusion derived into the sandbox goes with the premises it rested on, and its
justification with it. Retracting the extent wholesale is safe even though the sweep
reaches some of it first: a handle already gone is a no-op in :remove.
Answers {:removed-sentexes n :removed-justifications n}. Irreversible, hence the
! — that is the whole point of the control.
Discard the whole sandbox: every sentex in it, then the edge that made it a context,
and its roster entry.
One `edit`, so it is one settle, and the dependency-directed sweep does the rest — a
conclusion derived into the sandbox goes with the premises it rested on, and its
justification with it. Retracting the extent wholesale is safe even though the sweep
reaches some of it first: a handle already gone is a no-op in `:remove`.
Answers `{:removed-sentexes n :removed-justifications n}`. Irreversible, hence the
`!` — that is the whole point of the control.(token-of req)This request's session token — the one wrap-session put on it, else the cookie's
when its tag checks, else nil.
This request's session token — the one `wrap-session` put on it, else the cookie's when its tag checks, else nil.
(wrap-session handler kb-of)Give every request a session token, minting one into a cookie when the request
carries none whose tag checks, and record the use against the KB kb-of answers.
The token alone writes nothing — it names a sandbox that may not exist yet. Only
open creates the context, and only a write calls it.
Give every request a session token, minting one into a cookie when the request carries none whose tag checks, and record the use against the KB `kb-of` answers. The token alone writes nothing — it names a sandbox that may not exist yet. Only `open` creates the context, and only a write calls it.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |