Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ used ref is stamped as `KIT_CHANNEL` in `install.conf` and followed by
- The security model is deliberately **soft-only** — never re-introduce
OS-level deny ACLs. Background: `docs/design/ddev-working.md`,
current model: `docs/concepts/security-model.md`.
- **The kit never edits user-owned system config on its own — above all
`/etc/wsl.conf`.** Install/update only *show* snippets or name opt-in
commands the user runs themselves; `opk wsl-add-opencode-1-fix` is the
only command that writes wsl.conf, and only on explicit invocation.
`opk uninstall` asks (or `--yes`) before removing kit-owned wsl.conf
content. Rationale: `docs/design/wsl-conf-consent.md`.

## Testing

Expand Down
34 changes: 20 additions & 14 deletions docs/concepts/security-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,13 @@ do not distinguish WSL users, so with the default world-readable mount
(`.ssh/`, `NTUSER.DAT`, browser data). The kit's UID separation only covers
the Linux side.

The kit surfaces this everywhere: `install.sh` warns and offers to restrict
the mount to your user (recommended; applies after `wsl --shutdown` from
Windows), `update.sh` prints a warning, `status.sh` reports the exposure,
and the **wrapper warns on every `opencode` start** until the restriction is
applied.
The kit surfaces this everywhere: `install.sh` prints the ready-to-run
restriction snippet (the kit never edits `/etc/wsl.conf` itself — you
apply it; takes effect after `wsl --shutdown` from Windows),
`update.sh` prints a warning with the same snippet, `status.sh` reports
the exposure, and the **wrapper warns on every `opencode` start** until
the restriction is applied. Rationale for the no-write rule:
[design: wsl.conf consent](../design/wsl-conf-consent.md).

Manual fix via `/etc/wsl.conf`:

Expand Down Expand Up @@ -109,12 +111,15 @@ printing URL and device code.

The kit solves this **without granting the agent anything**:

- install.sh/update.sh keep a kit-managed **comment block** at the **top**
of `/etc/wsl.conf`. Its last line is a carrier for `open`'s scan: the
line starts with `#` (so WSL treats it as a comment) and contains a raw
carriage return before `root = <kit library>` — `open`'s regex cannot
see the `#` across that CR, so its first-match scan resolves into the
kit library and the computed powershell path lands there.
- `sudo opk wsl-add-opencode-1-fix` — run by you, the only kit command
that ever writes `/etc/wsl.conf` (install/update deploy just the
stand-in tree; the kit never edits the file implicitly) — writes a
kit-managed **comment block** at the **top** of `/etc/wsl.conf`. Its
last line is a carrier for `open`'s scan: the line starts with `#`
(so WSL treats it as a comment) and contains a raw carriage return
before `root = <kit library>` — `open`'s regex cannot see the `#`
across that CR, so its first-match scan resolves into the kit library
and the computed powershell path lands there.
- there, `wsl/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe` is
a harmless stand-in (`bin/browser-bridge`): it forwards to the **real**
powershell.exe whenever the calling user may execute it (you, the
Expand Down Expand Up @@ -142,9 +147,10 @@ no-op path triggers, it prints a one-line hint to the terminal
(`auto-open unavailable for the agent — open the printed URL manually`);
`OPK_BROWSER_BRIDGE_DEBUG=1` traces the decision for direct invocations
(self-test: [troubleshooting](../troubleshooting.md)).
`opk status` reports the bridge state; the wrapper warns when the mount is
restricted but the bridge is missing (hand-edited `wsl.conf`, partial
deploy). Uninstall removes the block and the stand-in tree.
`opk status` reports the bridge state and names
`opk wsl-add-opencode-1-fix` when the carrier is missing; the wrapper
warns the same way on every start. Uninstall asks before removing the
block (or assumes yes with `--yes`) and always removes the stand-in tree.

## Other root-equivalent surfaces (audit)

Expand Down
64 changes: 64 additions & 0 deletions docs/design/wsl-conf-consent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# wsl.conf consent — the kit never writes /etc/wsl.conf implicitly

> Status: **CURRENT.** Decided after
> [issue #100](https://github.com/steffenmaechtel/opencode-permissions-kit/issues/100)
> (kit 0.0.36 wrote an invalid section into `/etc/wsl.conf` and, on WSL
> ≤ 2.9.12, silently disabled *every* WSL setting in the file). Branch:
> `feature/wsl-conf-user-consent`.

## 1. Problem

Two kit features used to write into `/etc/wsl.conf` during
install/update:

- the `[automount]` /mnt/c hardening (install.sh, after a yes/no prompt),
- the browser-bridge carrier that keeps opencode 1.x device logins alive
on a hardened /mnt/c (install.sh/update.sh, no prompt at all).

`/etc/wsl.conf` is user-owned system configuration with boot-time
effects on the whole distro. Issue #100 showed what an implicit write
costs when it goes wrong: WSL aborted parsing the file and the user's
own settings (systemd, automount restriction) silently stopped applying.
A yes/no prompt inside a long install flow is not informed consent for
that class of change — and there was no prompt at all for the carrier.

## 2. Decision

The kit **never writes `/etc/wsl.conf` on its own**. The only paths that
ever touch it:

1. **Explicit opt-in command:** `sudo opk wsl-add-opencode-1-fix` —
deploys the browser-bridge stand-in tree and writes the kit comment
block (pure comments, WSL-silent; see
[security model](../concepts/security-model.md)). Called by the user,
nothing else. status.sh and the wrapper advertise it whenever the
mount is hardened but the carrier is missing.
2. **Printed snippets:** install.sh prints the ready-to-run
`[automount]` hardening snippet (with the resolved uid/gid); applying
it is the user's manual step, like the documented manual fix.
3. **Removal of kit-owned content only, with consent:** update.sh strips
the broken legacy 0.0.36 section (regression cleanup — it restores the
user's file to a WSL-parseable state; never writes anything).
`opk uninstall` asks before removing the kit comment block (or assumes
yes with `--yes`); declining prints the exact line range so the user
can delete it by hand — after uninstall there is no `opk` left to do
it for them. Neither path ever writes kit content into the file.

An existing carrier (written by earlier kit versions or the opt-in
command) is deliberately left untouched by updates — removing it would
break working device logins. `AGENTS.md` carries this as a repo rule for
future features: user-owned system config is only ever changed through
opt-in commands or printed instructions.

## 3. Consequences

- Fresh installs land with `opk status` showing the bridge as
stand-in-only until the user opts in — one explicit command, and the
wrapper/status explain exactly when it is needed (hardened /mnt/c,
opencode 1.x). opencode 2.x never needs it (its `open` access-checks
powershell and falls back to xdg-open).
- The /mnt/c hardening UX loses one prompt and gains a printed snippet —
the wrapper already repeats the snippet on every start until applied.
- Uninstall behavior: the stand-in tree always goes; the kit block is
removed only after an explicit yes (or `--yes`), otherwise the exact
lines to delete are printed. User content never goes.
6 changes: 6 additions & 0 deletions docs/how-to/uninstall.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ Run it as your default user (it asks for `sudo` where needed). Options:

**Project file contents are untouched** — only owner and group revert.

On WSL, the uninstaller also **asks** whether to remove the kit's
browser-bridge block from `/etc/wsl.conf` (kit-owned comment lines only;
default: yes). Decline and it prints the exact line range to delete by
hand — the kit never edits `wsl.conf` without your say-so
([why](../design/wsl-conf-consent.md)).

## What stays behind (harmless)

- Shell RC hook lines (`~/.bashrc` / `~/.zshrc` / `~/.profile`, tagged
Expand Down
25 changes: 25 additions & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,30 @@

This page lists the kit's commands and flags.

## wsl-add-opencode-1-fix

Write the WSL browser-bridge carrier into `/etc/wsl.conf` (sudo — the
command elevates itself):

```bash
sudo opk wsl-add-opencode-1-fix
```

Keeps opencode 1.x device logins (`console login`) alive on a hardened
`/mnt/c`: the carrier is a comment block at the top of your `wsl.conf`
whose `root =` line wins the bundled `open` package's scan, redirecting
its powershell.exe lookup to the kit stand-in (forwards for you, exits 0
for the agent). Takes effect immediately — no `wsl --shutdown` needed,
WSL only ever sees comments. opencode 2.x needs no bridge (its `open`
access-checks powershell and falls back to xdg-open).

This is the **only** kit command that writes `/etc/wsl.conf` — install
and update never touch the file; they only deploy the stand-in tree and
tell you about this command ([why](../design/wsl-conf-consent.md)). The
command is idempotent and also cleans up the broken kit-0.0.36 section if
one is present. `opk uninstall` asks before removing the block again
(`--yes` assumes yes).

## The `opk` command

After installation, one command manages everything (works from anywhere in
Expand All @@ -14,6 +38,7 @@ opk update --binary
opk upgrade-opencode # just the opencode binary
opk ddev-hosts-add # in a ddev project dir
opk handover me .gotmp # mixed-owner tree -> yours again
opk wsl-add-opencode-1-fix # opt in to the WSL browser-bridge carrier
opk uninstall
opk help # commands + arguments overview
```
Expand Down
22 changes: 13 additions & 9 deletions docs/reference/files.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ This page lists every file and directory the kit manages, and every key in

## /etc/wsl.conf

Not kit-owned — your WSL configuration. The kit appends the `[automount]`
hardening (on your confirmation, applies after `wsl --shutdown`) and keeps
a kit-managed **comment block** at the top (WSL only: its carrier line —
a `#` comment containing a raw carriage return before `root = …` — wins
the `open` package's scan and redirects opencode's powershell lookup to
the browser bridge stand-in; WSL itself only ever sees comments, so no
warning, no restart needed). Uninstall removes exactly that block and
leaves your own entries untouched.
Never written by the kit — your WSL configuration stays yours: install
and update only *print* snippets for you to apply yourself
([why](../design/wsl-conf-consent.md)). One optional kit artifact can
live in it, written by a single explicit command:
`sudo opk wsl-add-opencode-1-fix` places the browser-bridge **comment
block** at the top (WSL only; its carrier line — a `#` comment containing
a raw carriage return before `root = …` — wins the `open` package's scan
and redirects opencode's powershell lookup to the browser bridge
stand-in; WSL itself only ever sees comments, so no warning, no restart
needed). The `[automount]` hardening is yours to add manually.
Uninstall asks before removing the kit block (or assumes yes with
`--yes`); your own entries always stay.

## /etc/opencode-permissions-kit/

Expand Down Expand Up @@ -76,7 +80,7 @@ The deployed library mirrors the repository layout
| `/usr/local/lib/opencode-permissions-kit/bin/setup-container-backend` | Rootless backend provisioning |
| `/usr/local/lib/opencode-permissions-kit/bin/socket-check` | Rootless socket probe (`test -S` only) |
| `/usr/local/lib/opencode-permissions-kit/bin/cwd-check` | Headless serve cwd probe (readable-for-opencode check) |
| `/usr/local/lib/opencode-permissions-kit/bin/browser-bridge` | WSL browser bridge stand-in (deployed into the `wsl/` tree, see below) |
| `/usr/local/lib/opencode-permissions-kit/bin/browser-bridge` | WSL browser bridge stand-in source — deployed into the `wsl/` tree (see below); re-deployed by `opk wsl-add-opencode-1-fix` |
| `/usr/local/lib/opencode-permissions-kit/wsl/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe` | WSL browser bridge: stand-in the bundled `open` package spawns for device logins — forwards to the real powershell.exe when the caller may execute it, `exit 0` otherwise (WSL only; see [security model](../concepts/security-model.md#wsl2-the-browser-bridge-login-survival-on-a-hardened-mntc)) |
| `/usr/local/lib/opencode-permissions-kit/sh/ddev-terminal.sh` | Sourced `ddev()` terminal function (hooked into the default user's rc files) |
| `/usr/local/lib/opencode-permissions-kit/sh/ddev-handover.sh` | Shared helper: `.ddev` + settings-dir chown |
Expand Down
44 changes: 30 additions & 14 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,9 @@ options = "uid=1000,gid=1000,dmask=027,fmask=037"
```

(replace `uid`/`gid` with your default WSL user's), then from Windows run
`wsl --shutdown` and restart WSL. Details: [security model](concepts/security-model.md).
`wsl --shutdown` and restart WSL. The kit never edits `/etc/wsl.conf`
itself — applying the snippet is yours ([why](design/wsl-conf-consent.md)).
Details: [security model](concepts/security-model.md).

## `opencode console login` / `opencode auth login` crashes with EACCES on powershell.exe

Expand All @@ -395,13 +397,21 @@ file itself is executable; plain `execve` is unaffected. The wrapper moves
starting the binary, so this is handled automatically; a crash means an
old wrapper is deployed.

**Fix:** re-run the install or `opk update` — both (re)apply the kit
comment block in `/etc/wsl.conf`, the stand-in under
`/usr/local/lib/opencode-permissions-kit/wsl/`, and the current wrapper.
The wsl.conf block takes effect immediately (only `open` reads it; WSL
sees comments — no `wsl --shutdown` needed). Without a bridge, the login
still works when you open the printed URL yourself: the crash happens
*after* URL and device code are displayed. Details: [security
**Fix:** run

```bash
sudo opk wsl-add-opencode-1-fix
```

It (re)deploys the stand-in under
`/usr/local/lib/opencode-permissions-kit/wsl/` and writes the kit comment
block into `/etc/wsl.conf` — the one kit command that touches the file,
and only because you ran it ([why](design/wsl-conf-consent.md)). The
block takes effect immediately (only `open` reads it; WSL sees comments
— no `wsl --shutdown` needed). If the stand-in itself is missing, run
`sudo opk update` first. Without a bridge, the login still works when you
open the printed URL yourself: the crash happens *after* URL and device
code are displayed. Details: [security
model](concepts/security-model.md).

**Diagnose — expected vs. broken:** when the login runs with the agent
Expand Down Expand Up @@ -432,12 +442,18 @@ stops applying: the `[automount]` restriction stays pending, `[boot]`
`systemd=true` (and with it the docker-rootless backend) disappears after
the next `wsl --shutdown` (issue #100).

**Fix:** update the kit (`sudo opk update`, ≥ 0.0.37) — the rewrite
replaces the broken section with a pure comment block that WSL never
complains about — then run `wsl --shutdown` from Windows and reopen your
distro so the dropped settings apply again. `opk status` should show the
backend socket reachable, `/mnt/c` restricted, and the browser bridge
deployed. Details: [security model](concepts/security-model.md).
**Fix:** update the kit (`sudo opk update`, ≥ 0.0.38) — it strips the
broken section (restoring WSL's ability to parse the file) — then opt in
to the login-fix carrier yourself:

```bash
sudo opk wsl-add-opencode-1-fix
```

Finally run `wsl --shutdown` from Windows and reopen your distro so the
dropped settings apply again. `opk status` should show the backend socket
reachable, `/mnt/c` restricted, and the browser bridge deployed. Details:
[security model](concepts/security-model.md).

## Group membership (opencode group) not applied

Expand Down
Loading
Loading