Repository navigation
docs(concepts): add the sessions concept guide #939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,260 @@ | ||
| --- | ||
| description: What a Minimal session is, how a provider creates and hosts one, and how sessions compose from your project config plus loadouts. | ||
| --- | ||
|
|
||
| # Sessions | ||
|
|
||
| A **session** is Minimal's isolated developer environment: a durable place to | ||
| work on a project, with exactly the tools and configuration that project | ||
| declares, and nothing else from your host leaking in unless you allow it. You | ||
| create a session for a project, attach a shell to it, and do your work inside | ||
| it; detach and the session keeps running in the background, ready to rejoin. | ||
| When you are done you tear it down. | ||
|
|
||
| Sessions are managed by `min`, the Minimal session CLI. This guide explains | ||
| what a session is, the provider that hosts it, how one is created and entered, | ||
| how it is composed from your project config plus loadouts, and how sessions | ||
| relate to sandboxes and tasks. For the exact flags of every command mentioned | ||
| here, see the [`min` command reference](../reference/cli-min.md). | ||
|
|
||
| ## Why sessions | ||
|
|
||
| Working directly on your host mixes every project's tools, environment | ||
| variables, and stray state into one global namespace. Minimal instead gives | ||
| each project its own isolated environment: | ||
|
|
||
| - **Reproducible.** A session's tools come from Minimal packages, built or | ||
| fetched deterministically from your project's declarative config. Two people | ||
| who activate the same project get the same environment. | ||
| - **Isolated.** Everything that runs in a session runs in a sandbox. Your | ||
| host filesystem, host environment, and other projects are not visible inside | ||
| it unless the project or your policy brings them in. | ||
| - **Declarative.** What a session contains is derived from files you commit | ||
| (your `minimal.toml` and the packages it references), not from setup steps you | ||
| run by hand and hope to remember. | ||
|
norrietaylor marked this conversation as resolved.
Resolved
|
||
|
|
||
| Declarative does not mean one-size-fits-all: **loadouts** let you bring your | ||
| own tools — your editor, your shell configuration — into a session without | ||
| those personal choices contaminating the project's declared environment. See | ||
| the [loadouts guide](./loadouts.md). | ||
|
|
||
| ## The provider and session model | ||
|
|
||
| Two roles are in play whenever you use sessions: | ||
|
|
||
| - A **session** is the durable environment itself: a single isolated | ||
| workspace tied to one project directory. | ||
| - A **provider** is the daemon that creates and hosts sessions. On Linux the | ||
| provider is `minimald`, an SSH server that owns session lifecycle and hosts | ||
| the shells and executions that run inside each session. | ||
|
|
||
| The `min` CLI is a thin client. It discovers the provider's socket, | ||
| auto-spawns a provider if none is running, and speaks to it over SSH on a UNIX | ||
| domain socket. You rarely interact with the provider directly: running a `min` | ||
| command is enough, and the provider comes up on demand. | ||
|
|
||
| On platforms where `minimald` cannot run natively (macOS, or a Linux setup that | ||
| opts into it), a second daemon, `minvmd`, boots a lightweight Linux microVM and | ||
| runs `minimald` inside it, bridging the connection so `min` reaches the session | ||
| without needing to know a VM is involved. From the user's point of view the | ||
| model is identical: a provider creates and hosts sessions, and `min` talks to | ||
| it. | ||
|
|
||
| ## Creating and entering a session | ||
|
|
||
| You create a session by **activating** a project. From the project directory: | ||
|
|
||
| ```console | ||
| $ min activate --attach | ||
|
norrietaylor marked this conversation as resolved.
Resolved
|
||
| ``` | ||
|
|
||
| `min activate` takes a project path that defaults to the current directory. | ||
| `--attach` drops you straight into a shell in the new session once it is ready. | ||
| Without `--attach`, activation prints the new session's id and returns, leaving | ||
| the session ready in the background for you to attach to later. | ||
|
|
||
| A few things happen during activation: | ||
|
|
||
| 1. `min` resolves the project path and, if the project has no `minimal.toml` | ||
| yet, offers to scaffold one (the session still comes up with a default | ||
| environment either way). | ||
| 2. Your project files are uploaded into the session's workspace on the provider | ||
| so it can read your config. You can opt out of this with `--sync none`. | ||
| 3. The session is composed from your project config plus any loadouts (see the | ||
| next section), and any environment values that need your approval are gated | ||
| against your user policy. | ||
|
|
||
| Useful activation options: | ||
|
|
||
| - `--name <NAME>` gives the session a stable, human-friendly name you can use | ||
| in place of its id. | ||
| - `--network <MODE>` selects the network mode: `no-net`, `host-net` (the | ||
|
norrietaylor marked this conversation as resolved.
Resolved
|
||
| default), or `own-ip` (a dedicated IP on Minimal's virtual subnet). With | ||
| `own-ip` you can also publish `--ingress EXT:INT[/PROTO]` port mappings. | ||
| Platform support is still uneven: `own-ip` does not currently work with the | ||
| native Linux provider, and on macOS `host-net` carries outbound traffic | ||
| only, because the session lives inside a VM. | ||
| - `--loadout <NAME>` applies a named loadout (repeatable); `--no-loadouts` | ||
| applies none. | ||
| - `--no-prompt` fails instead of prompting when composition surfaces items your | ||
| policy cannot auto-decide, printing a ready-to-paste policy snippet instead. | ||
| This mode is selected automatically when stdout or stderr is not a terminal, | ||
| so activation is safe to run from scripts and CI. | ||
|
|
||
| To enter a session that already exists, **attach** to it by id or name: | ||
|
|
||
| ```console | ||
| $ min attach my-session | ||
| ``` | ||
|
|
||
| Attach opens an interactive shell inside the session, with your tools on `PATH` | ||
| and the session's workspace (your project files, uploaded at activation) as your | ||
| working directory. Exiting 8000 the shell detaches: the session keeps running in the | ||
| background, and `min attach` rejoins it later. Changes you make inside stay in | ||
| the session's workspace; bring them back to the host by pushing them out with | ||
| `git push min://<session>`. | ||
|
|
||
| To run a declared task non-interactively instead of opening a shell, pass | ||
| `--command` (short `-c`). The command channel accepts only `min run <task name>` | ||
| invocations; arbitrary commands need an interactive shell: | ||
|
|
||
| ```console | ||
| $ min attach my-session -c 'min run test' | ||
| ``` | ||
|
|
||
| ## How a session is composed | ||
|
norrietaylor marked this conversation as resolved.
Resolved
|
||
|
|
||
| A session's contents are not hand-configured. They are **composed** from | ||
| several declarative sources into a single result the provider applies when it | ||
| mints your shell. The composition is a pipeline that spans the `min` client and | ||
| the provider: | ||
|
|
||
| 1. **Your loadouts, composed on the client.** A loadout is a reusable bundle of | ||
| session contributions (environment variables, file mappings, package | ||
| selections, and lifecycle hooks) kept in your user config. `min` composes the loadouts you | ||
| selected, resolving values and gating them against your user policy. See the | ||
| [loadouts guide](./loadouts.md) for how to write and select them. | ||
| 2. **Your project config, collected on the provider.** The provider reads your | ||
| project's `minimal.toml` from the uploaded workspace: its session block, the | ||
| packages the project's stack declares as build and runtime dependencies, and | ||
| any environment the stack contributes. | ||
| 3. **Package contributions, collected on the provider.** Packages in the | ||
| project's dependency closure can contribute environment wiring and file | ||
| mappings of their own. These are collected alongside the project's own | ||
| contributions. | ||
|
|
||
| The provider routes anything that needs your approval (chiefly environment | ||
| values sourced from your own environment, and file patches) back to `min`, | ||
| which gates them against your user policy and, when the policy cannot decide, | ||
| prompts you. Approved items, plus the selections that were already decided, are | ||
| assembled into the final composition. The result carries four kinds of item: | ||
|
|
||
| - **Variables**: environment variables set inside the session. | ||
| - **File patches**: files copied into the session's home when it is finalized. | ||
| - **Packages**: the tools and libraries that make up the environment. | ||
| - **Lifecycle hooks**: scripts declared to run at defined points in a | ||
| session's life. Hooks are inert in the current release: a hook declared in | ||
| a project's `[session]` block is composed but never executed, and hooks | ||
| declared in loadouts are excluded from composition entirely. | ||
|
|
||
| A key principle: **your user policy is enforced only on the client.** The | ||
| provider never runs your policy. It forwards the items that need a decision, and | ||
| `min` applies your allow, deny, and ignore rules, prompting you for anything | ||
| left undecided. Every surviving item keeps a record of which source contributed | ||
| it, so a session's contents can always be traced back to your loadout, your | ||
| project, or a specific package. | ||
|
|
||
| Environment values that carry your own environment (for example a variable your | ||
| loadout inherits from your shell) are the ones your policy gates, because they | ||
| move data from your host into the sandbox. Values a package or project hardcodes | ||
| as literals are a matter of that declaration, not of your policy, and pass | ||
| through without prompting. | ||
|
|
||
| ## Session lifecycle | ||
|
|
||
| List the sessions the provider is currently hosting: | ||
|
|
||
| ```console | ||
| $ min ls | ||
| ``` | ||
|
|
||
| `min ls` prints a table of session ids, names, titles, and last activity, along | ||
| with the shared resource pool. `--raw` prints bare ids one per line for | ||
| scripting, and `--json` prints the full list as JSON. | ||
|
|
||
| Rename a session: | ||
|
|
||
| ```console | ||
| $ min rename my-session backend-work | ||
| ``` | ||
|
|
||
| Destroy a session when you are finished with it: | ||
|
|
||
| ```console | ||
| $ min destroy my-session | ||
| ``` | ||
|
|
||
| `min destroy` terminates a single session by id or name. `min destroy --all` | ||
| tears down every session at once (add `--force` to skip the confirmation). | ||
|
|
||
| Stop the provider itself: | ||
|
|
||
| ```console | ||
| $ min stop | ||
| ``` | ||
|
|
||
| `min stop` shuts down the active provider. Stopping the provider ends | ||
| every running shell, but the sessions themselves survive: their records and | ||
| workspaces persist, and a provider that comes back up re-hosts them (the extras | ||
| composed from loadouts are rebuilt when you re-activate). The provider refuses | ||
| to shut down while any session is busy (a live shell, or an activation in | ||
| flight) unless you pass `--force`. Contrast this with `min destroy`, which | ||
| removes one session entirely and leaves the provider running to host the rest. | ||
| Because `min` auto-spawns a provider on demand, the next `min` command after a | ||
| stop simply brings one back up. | ||
|
|
||
| You can inspect a session's effective networking policy as JSON: | ||
|
|
||
| ```console | ||
| $ min session policy my-session | ||
| ``` | ||
|
|
||
| And list the loadouts available in your user config: | ||
|
|
||
| ```console | ||
| $ min loadout list | ||
| ``` | ||
|
|
||
| ## Sessions, sandboxes, and tasks | ||
|
|
||
| A session is not itself a sandbox: it is the managed, named environment the | ||
| provider hosts. But everything that runs *inside* a session runs in a | ||
| **sandbox**, the low-level isolation primitive built from Linux user and mount | ||
| namespaces. The first time you attach a shell, the provider composes a sandbox | ||
| whose root filesystem is assembled from the package closure your session needs | ||
| (each package's files hardlinked into place), mounts the session's workspace | ||
| (your uploaded project files) into it, sets the composed environment variables, | ||
| and launches your shell there. Detaching leaves that sandbox running; | ||
| reattaching joins it. The sandbox is how the session's isolation and | ||
| reproducibility are actually delivered. | ||
|
|
||
| **Tasks** run in sandboxes of their own. A task is a command your project | ||
| defines in its `minimal.toml`: a named, repeatable operation (a build step, a | ||
| test run, a linter) that executes in a freshly composed sandbox built from the | ||
| packages that task needs. If a needed package is not present, Minimal builds it | ||
| or fetches it from a remote cache first. Loadouts contribute to the session's | ||
| interactive shell only, never to tasks, so your personal tooling cannot | ||
| influence a task's deterministic execution. Running `min attach -c 'min run | ||
| <task>'` is the non-interactive way to trigger a declared task against a | ||
| session; arbitrary commands need an interactive shell. | ||
|
norrietaylor marked this conversation as resolved.
Resolved
|
||
|
|
||
| The through-line: a **session** is the durable, named environment a provider | ||
| hosts; a **sandbox** is the isolated execution context composed from a package | ||
| closure; and a **task** is a declared command that runs in such a sandbox. Your | ||
| project config and loadouts decide what goes into all three. | ||
|
|
||
| ## See also | ||
|
|
||
| - [`min` command reference](../reference/cli-min.md) for every command and flag. | ||
| - [Loadouts](./loadouts.md) for writing and selecting reusable session | ||
| contributions. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.