cdsr: initial import of deployed stack (runtime 4.133.0, pinned extensions, SSOT vault)

This commit is contained in:
Antigravity
2026-08-20 10:49:50 +02:00
commit c2d92d7b74
20 changed files with 1440 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
---
title: 🏗️ Architecture
description: Map of the code-server runtime topology, request flow, and local directory layout.
source: verified deployment on the loca operator host, 2026-08-19
reviewed: 2026-08-19
---
# 🏗️ Architecture
The page set starts with architecture flow and layout, then routes to operations or reference for maintenance and path-level lookup.
- [[architecture/runtime-topology]] — request-path topology, local directory map, auth/cert rationale, and websocket requirements
- [[operations/index]] — operations starting point
- [[reference/config-paths]] — canonical config/service/file locations

View File

@@ -0,0 +1,90 @@
---
title: 🧭 Runtime Topology
description: Request flow, local directory map, auth/cert rationale, process state, and websocket requirements for the cdsr.loca.zone code-server deployment.
source: verified deployment on the loca operator host, 2026-08-19
reviewed: 2026-08-19
---
# 🧭 Runtime Topology
## Request path
```mermaid
flowchart LR
Browser[Browser] --> Nginx443[Nginx 443\nTLS + security headers]
Nginx443 --> Authelia[Authelia auth_request\nauth.loca.zone one_factor]
Authelia --> CodeServer[code-server on 127.0.0.1:52224]
```
- Browser → Nginx `cdsr.loca.zone:443`
- TLS terminates at Nginx
- security headers included for app traffic
- forwards requests to code-server only after auth_request
- Nginx → Authelia
- `authelia_location` and `authelia_authrequest` snippets included
- `auth_request` policy for `cdsr.loca.zone` is `one_factor`
- request allowed only after successful Authelia authentication
- Authelia → code-server
- reverse proxy to `127.0.0.1:52224`
- loopback target: `bind-addr` is not public
- headers forwarded: `Host`, `X-Real-IP`, `X-Forwarded-For`, `X-Forwarded-Proto`
- websocket headers: `proxy_http_version 1.1`, `Upgrade $http_upgrade`, `Connection $connection_upgrade`
- `proxy_read_timeout 86400`
## Local directory map
- `/home/loca/dev/code-server`
- `config/`
- `scripts/`
- `deploy/`
- `data/user/`
- `data/extensions/`
- `tmp/extensions/`
- `q5vault/`
- `/etc/systemd/system/cdsr.service`
- `/etc/nginx/sites-available/cdsr.loca.zone`
- `/etc/letsencrypt/live/cdsr.loca.zone/`
- `/home/loca/dev/wikis/cdsr/{content,quartz.config.yaml,current}`
## Why `auth: none` and `cert: false`
- `bind-addr` is `127.0.0.1:52224`, so code-server never binds a public interface
- `auth: none` is correct because Authelia performs edge auth; enabling code-server auth would add a second credential store, break websocket UX, and duplicate login logic
- `cert: false` is correct because TLS is terminated at Nginx with a Let's Encrypt lineage certificate
- certs are in `/etc/letsencrypt/live/cdsr.loca.zone/` for Nginx TLS termination
- Authelia handles identity at the edge with `auth_request` before requests enter code-server
- this is only safe because code-server is loopback-only
## Process / state facts
- system scope unit: `cdsr.service`
- `User=loca`
- `Group=loca`
- `Restart=always`
- `WorkingDirectory=/home/loca/dev/code-server`
- editor state is project-isolated by explicit config, not by default: `config/config.yaml` sets `user-data-dir: /home/loca/dev/code-server/data/user` and `extensions-dir: /home/loca/dev/code-server/data/extensions`, and the unit passes `--config /home/loca/dev/code-server/config/config.yaml`
- `data/user` and `data/extensions` are therefore inside the project
- `user-data-dir` does not capture everything — see the next section for what lands outside it anyway
- packaged unit `code-server@loca` is deliberately disabled
## State outside the project
The unit sets `Environment=HOME=/home/loca`, so anything that resolves its own path from `$HOME` ignores `user-data-dir` entirely.
| Path | Written by / why | Deployment-owned |
| --- | --- | --- |
| `data/user/` | code-server, from `user-data-dir` in `config/config.yaml` | yes, in-project |
| `data/extensions/` | code-server, from `extensions-dir` in `config/config.yaml` | yes, in-project |
| `/home/loca/.local/share/code-server/heartbeat` | the running server, regardless of `user-data-dir` | yes, but outside the project |
| `/home/loca/.local/share/code-server/coder-logs/` | same server process, default log location | yes, but outside the project |
| `/home/loca/.continue/` | the Continue extension on first activation, resolved from `$HOME` | no — extension-owned, holds its real config and chat history |
| `/home/loca/.claude/`, `/home/loca/.claude.json` | pre-existing operator state shared with the host Claude Code CLI | no — operator-wide, not created by the extension install, never delete as part of cdsr maintenance |
- practical consequence: the deployment is reproducible from the project directory for the editor, extension binaries and editor settings, but extension-owned configuration and chat history are not, so a backup of `/home/loca/dev/code-server` alone is incomplete — `~/.continue` has to be archived alongside it
## Websocket + long-poll requirement
- Nginx must proxy websocket upgrades for terminal, shell, and extension transport:
- `proxy_http_version 1.1`
- `proxy_set_header Upgrade $http_upgrade`
- `proxy_set_header Connection $connection_upgrade`
- keep long sessions with `proxy_read_timeout 86400`

91
q5vault/index.md Normal file
View File

@@ -0,0 +1,91 @@
---
title: 💻 Code Server SSOT
description: Single source of truth for the SSO-gated code-server IDE at cdsr.loca.zone, its pinned extension baseline, and its operations.
source: verified deployment on the loca operator host, 2026-08-19
reviewed: 2026-08-19
---
# 💻 Code Server SSOT
Browser IDE for user `loca`, reverse-proxied by Nginx, gated by Authelia one-factor login, running loopback-only on `127.0.0.1:52224`. Extensions come from Open VSX and are pinned in a lock file, never installed ad hoc.
## 🚀 Quick start
- open: https://cdsr.loca.zone
- login at `auth.loca.zone` first (one-factor), then the editor shell loads
- wiki (public, no login): https://wiki.cdsr.loca.zone
- service: `cdsr.service` (system scope, `User=loca`)
- `systemctl status cdsr --no-pager`
- `sudo -n systemctl restart cdsr`
- health: `curl -sf http://127.0.0.1:52224/healthz`
- project root: `/home/loca/dev/code-server`
- config: `config/config.yaml`
- extension pins: `config/extensions.lock.json`
- runtime pin: `config/runtime-version.txt`
- state: `data/user/`, `data/extensions/`
## 🔐 Auth flow
- Browser → Nginx `cdsr.loca.zone:443` → `auth_request` to Authelia → code-server `127.0.0.1:52224`
- Authelia rule: `cdsr.loca.zone` = `one_factor`; default policy stays `bypass`, so the wiki host needs no rule and stays public
- code-server runs `auth: none` + `cert: false` on purpose: TLS and identity are terminated in front of it, and it never listens on a public interface
## 🧩 Extension baseline
| Extension | Version | Role |
| --- | --- | --- |
| `Continue.continue` | 2.1.0 | open-source AI agent, chat, edit, autocomplete |
| `Anthropic.claude-code` | 2.1.235 | first-party Claude Code IDE integration |
| `ms-python.python` | 2026.4.0 | Python language support |
| `ms-python.debugpy` | 2026.6.0 | Python debugger, pinned explicitly |
| `ms-toolsai.jupyter` | 2025.9.1 | notebooks in the browser IDE |
| `ms-azuretools.vscode-containers` | 2.4.5 | Container Tools, successor to the old Docker extension |
- install or repair: `cd /home/loca/dev/code-server && ./scripts/install-extensions.sh`
- audit against the lock file: `./scripts/list-extension-versions.sh`
- optional, enterprise-only: `sourcegraph.cody-ai` 1.155.0 via `./scripts/install-extensions.sh --include-optional`
- AI models come from the host LiteLLM gateway (`127.0.0.1:52001`), not a cloud sign-in; use the `free` model — see [[operations/extensions]]
- a fresh browser profile starts in Restricted Mode and hides every third-party extension until the folder is trusted — see [[operations/extensions]]
- details: [[operations/extensions]]
## ⬆️ Upgrade sequence
```bash
cd /home/loca/dev/code-server/tmp
curl -fsSL -O https://github.com/coder/code-server/releases/download/v<NEW>/code-server_<NEW>_amd64.deb
sudo -n dpkg -i code-server_<NEW>_amd64.deb
echo "<NEW>" > /home/loca/dev/code-server/config/runtime-version.txt
cd /home/loca/dev/code-server
./scripts/install-extensions.sh
./scripts/list-extension-versions.sh
sudo -n systemctl restart cdsr
```
Extensions land on disk before the restart, so the restart is what loads them into the extension host; reload the browser window afterwards if a session was open. The currently deployed pin is 4.133.0. Full procedure: [[operations/install-upgrade]].
## 🔀 Dev server routing
- default: start a dev server on `127.0.0.1:<port>`, then open `https://cdsr.loca.zone/proxy/<port>/`
- path-sensitive frameworks: use `https://cdsr.loca.zone/absproxy/<port>/` and set the framework's base path
- pretty per-port subdomains are intentionally not configured; see [[reference/dev-routing]]
## ✅ Do / don't
- do: install extensions only from Open VSX through `config/extensions.lock.json`
- do: keep the bind address loopback-only and let Nginx own TLS
- do: sign in to AI providers inside the running editor UI
- don't: commit provider tokens, cookies, or extension secrets into this project
- don't: enable the packaged `code-server@loca` unit; state belongs in `data/`
- don't: add an Authelia rule for `wiki.cdsr.loca.zone`; the wiki is public by design
## Start here
| Need | Page |
| --- | --- |
| Understand the deployed shape | [[architecture/runtime-topology]] |
| Install or upgrade the runtime | [[operations/install-upgrade]] |
| Fix auth or TLS | [[operations/auth-tls]] |
| Manage the extension baseline | [[operations/extensions]] |
| Re-run proof checks | [[operations/verification]] |
| Look up a path or port | [[reference/config-paths]] |
| Expose a dev server | [[reference/dev-routing]] |

View File

@@ -0,0 +1,99 @@
---
title: 🔐 Auth & TLS
description: Authelia access rule, nginx four-block topology, shared certificate lineage, proof commands, and rollback/debug order for the code-server SSO-gated stack.
source: verified deployment on the loca operator host, 2026-08-19
reviewed: 2026-08-19
---
# 🔐 Auth & TLS
## Authelia access rule
File: `/home/loca/dev/services/authelia/config/configuration.yml` (root:root 644)
Edit procedure — stage in `/tmp` then install atomically:
```bash
sudo -n install -m 644 -o root -g root /tmp/configuration.yml /home/loca/dev/services/authelia/config/configuration.yml
```
YAML added inside `access_control.rules`:
```yaml
- domain: "cdsr.loca.zone"
policy: one_factor
```
The rule sits alongside existing rules for `auth.loca.zone`, `admin.loca.zone`, `omp.loca.zone`, `pwiz.loca.zone`, `trlm.loca.zone`.
## Why the wiki has no rule
Authelia runs with `default_policy: bypass`. An explicit rule for `wiki.cdsr.loca.zone` is intentionally omitted — the wiki remains public by omission while the app host becomes protected by explicit allowlisting.
## Apply procedure
```bash
cd /home/loca/dev/services/authelia
sudo -n docker compose config
sudo -n docker compose restart
sudo -n docker compose ps
```
Authelia v4.39.20 restarts clean on loopback `127.0.0.1:51091`. Session cookie domain `loca.zone`, `authelia_url https://auth.loca.zone`, expiration 1h, inactivity 5m.
## Nginx topology
File: `/etc/nginx/sites-available/cdsr.loca.zone` (symlinked into `sites-enabled`), installed from `deploy/cdsr.nginx.conf`.
Four server blocks after Certbot:
- `cdsr.loca.zone:443` — reverse proxy to `http://127.0.0.1:52224`
- includes `snippets/security-headers-app.conf`
- includes `snippets/authelia-location.conf`
- `location /` protected by `snippets/authelia-authrequest.conf`
- `proxy_http_version 1.1`, websocket headers (`Upgrade`, `Connection`), `proxy_read_timeout 86400`
- `wiki.cdsr.loca.zone:443` — static root `/home/loca/dev/wikis/cdsr/current`
- includes `snippets/security-headers.conf`
- 1y immutable cache for `css|js|png|webp|ico|json|woff2?|svg|map`
- `try_files $uri $uri.html $uri/ =404`
- `cdsr.loca.zone:80` — ACME challenge from `/var/www/html` + 301 to HTTPS
- `wiki.cdsr.loca.zone:80` — ACME challenge from `/var/www/html` + 301 to HTTPS
## TLS certificate
Single lineage `cdsr.loca.zone` covering both hosts:
```bash
sudo -n certbot certonly --webroot -w /var/www/html --non-interactive --agree-tos -m admin@loca.zone --cert-name cdsr.loca.zone -d cdsr.loca.zone -d wiki.cdsr.loca.zone
```
Expiry: 2026-11-17, auto-renew scheduled. Certs at `/etc/letsencrypt/live/cdsr.loca.zone/{fullchain,privkey}.pem`.
If a partial lineage already exists, rerun with the same `--cert-name` and `--expand` instead of minting a second lineage.
`sudo -n nginx -t` emits a harmless pre-existing `ssl_stapling` warning about `flxr.loca.zone` — expected and safe to ignore.
## Proof commands
```bash
# app host — unauthenticated redirect into Authelia
curl -sSI https://cdsr.loca.zone/
# -> HTTP/1.1 302
# -> Location: https://auth.loca.zone/?rd=https%3A%2F%2Fcdsr.loca.zone%2F&rm=HEAD
# wiki host — public, no auth redirect
curl -sSI https://wiki.cdsr.loca.zone/
# -> HTTP/1.1 200
# -> serves static content directly
```
## Rollback / debug order if auth breaks
1. `sudo -n nginx -t` — config clean (ignore the pre-existing flxr ssl_stapling warning)
2. Confirm app 443 block includes both snippets:
- `include snippets/authelia-location.conf`
- `location / { include snippets/authelia-authrequest.conf; ... }`
3. Confirm Authelia rule present:
- `grep -A2 'domain: "cdsr.loca.zone"' /home/loca/dev/services/authelia/config/configuration.yml`
4. Check Authelia container:
- `cd /home/loca/dev/services/authelia && sudo -n docker compose ps`
- `cd /home/loca/dev/services/authelia && sudo -n docker compose logs --tail=50 authelia`
5. Prove code-server itself is healthy (bypasses all auth/TLS):
- `curl -sf http://127.0.0.1:52224/healthz`
- should return `{"status":"alive",...}` when an editor session is active

View File

@@ -0,0 +1,125 @@
---
title: 🧩 Extensions
description: Pinned Open VSX extension baseline, lock file semantics, install/audit commands, optional/excluded entries, and the LiteLLM AI provider wiring.
source: verified deployment on the loca operator host, 2026-08-19; AI wiring verified in the running IDE 2026-08-20
reviewed: 2026-08-20
---
# 🧩 Extension baseline
## Pinned baseline (six entries)
| id | version | platform | reason |
|---|---|---|---|
| `Continue.continue` | 2.1.0 | linux-x64 | Primary open-source AI agent/chat/edit/autocomplete extension. |
| `Anthropic.claude-code` | 2.1.235 | linux-x64 | First-party Claude Code IDE integration. |
| `ms-python.python` | 2026.4.0 | universal | Python language support expected in this environment. |
| `ms-python.debugpy` | 2026.6.0 | linux-x64 | Explicit debugger dependency instead of relying on later bootstrap by the Python extension. |
| `ms-toolsai.jupyter` | 2025.9.1 | universal | Notebook workflow support in the browser IDE. |
| `ms-azuretools.vscode-containers` | 2.4.5 | universal | Current Container Tools extension; its README states it replaces the older Docker extension functionality. |
All six come from `config/extensions.lock.json` (registry: `https://open-vsx.org`).
## Lock file mechanics
`config/extensions.lock.json` is the single source of truth. Each entry contains `id`, `version`, `platform`, `vsix_url`, `sha256_url`, `reason`, and `mode` (`baseline` or `optional`).
VSIX cache lives in `tmp/extensions/` with filenames `<id>-<version>@<platform>.vsix`.
Install script (`scripts/install-extensions.sh`):
- requires `python3` (no `jq` on this host)
- downloads VSIX + `.sha256` into cache if not present
- verifies sha256 before install
- installs with `code-server --config config/config.yaml --install-extension <abs .vsix> --force`
Audit script (`scripts/list-extension-versions.sh`):
- runs `code-server --list-extensions --show-versions`
- compares against lock file
- exits non-zero on `MISSING` or `MISMATCH`
- `--include-optional` extends the check to `mode: optional` entries
## Commands
```bash
# install baseline only
cd /home/loca/dev/code-server && ./scripts/install-extensions.sh
# install baseline + optional
./scripts/install-extensions.sh --include-optional
# audit baseline
./scripts/list-extension-versions.sh
# audit baseline + optional
./scripts/list-extension-versions.sh --include-optional
```
## Transitively installed (expected, unpinned)
| id | version | note |
|---|---|---|
| `ms-python.vscode-python-envs` | 1.36.0 | Python extension dependency |
| `ms-toolsai.jupyter-keymap` | 1.1.2 | Jupyter dependency |
| `ms-toolsai.jupyter-renderers` | 1.3.0 | Jupyter dependency |
| `ms-toolsai.vscode-jupyter-cell-tags` | 0.1.9 | Jupyter dependency |
| `ms-toolsai.vscode-jupyter-slideshow` | 0.1.6 | Jupyter dependency |
These are not in the lock file and will vary; they are expected by-products of the baseline install.
## Optional: Cody (enterprise only)
| id | version | platform | reason |
|---|---|---|---|
| `sourcegraph.cody-ai` | 1.155.0 | universal | Useful only with a Cody Enterprise account; the Open VSX README states it is no longer available for non-enterprise users. |
Opt-in:
```bash
./scripts/install-extensions.sh --include-optional
```
## Exclusions (recorded in lock file)
| id | reason |
|---|---|
| `TabNine.tabnine-vscode` | Open VSX listing marks it legacy and states it does not onboard new users. |
| `GitHub.copilot` | Microsoft-marketplace-only AI tooling; code-server cannot rely on Microsoft marketplace terms or access. |
Any Microsoft-marketplace-only AI extensions fall under the same exclusion.
## Where extension configuration actually lives
Extension config is not under `data/user/`. The unit sets `HOME=/home/loca`, so extensions that resolve their own paths from `$HOME` write outside the project — see [[architecture/runtime-topology#state-outside-the-project]].
- Continue writes `/home/loca/.continue/` — its real configuration and history, not `data/user/`
- `config.yaml` — mode 600, holds provider settings; never commit it, never paste its contents into the wiki or project files
- also `config.ts`, `.continuerc.json`, `index/`, `sessions/`, `dev_data/`, `.utils/`
- version nuance for the pinned 2.1.0 build
- it still contributes `continue.remoteConfigServerUrl`, `continue.userToken` and `continue.remoteConfigSyncPeriod` for shared/team config sync, sync period defaulting to 60 minutes
- none of those three carries a deprecation message; the only deprecated setting is `continue.telemetryEnabled`, whose deprecation message points at Telemetry > Allow Anonymous Telemetry on the extension's User Settings page
- so Continue configuration is not local-only in this build; this deployment simply leaves remote sync unconfigured
- Claude Code reuses the operator's pre-existing `/home/loca/.claude/` and `/home/loca/.claude.json`, shared with the host Claude Code CLI — it is not deployment-owned state and is not created by the extension install
- backup targets: `/home/loca/dev/code-server/` plus `/home/loca/.continue/`; the project directory alone misses Continue's config and chat sessions
## AI provider wiring (LiteLLM gateway)
Continue is wired to the host LiteLLM gateway (`lllm`, loopback `127.0.0.1:52001`) — no cloud provider sign-in is used. The gateway is reachable from the IDE because code-server and LiteLLM run on the same host.
- credential: a dedicated LiteLLM virtual key with alias `cdsr-ide`, scoped to the three model groups below. The master key is never placed in a client config.
- key file: `/home/loca/dev/code-server/.ai-credentials` (mode 600, gitignored, single line `litellm_ide_key=`). The value is also present in `/home/loca/.continue/config.yaml` (mode 600). Neither value belongs in this wiki, the repo, or a systemd unit.
- client config: `/home/loca/.continue/config.yaml`, three models, all `provider: openai` against `apiBase: http://127.0.0.1:52001/v1`. Shape validated against the extension's own `config-yaml-schema.json` (`name`, `model`, `provider` required; `roles` enum accepts `chat`, `edit`, `apply`, `autocomplete`).
| model | upstream | roles | live state |
|---|---|---|---|
| `free` | OpenRouter `nvidia/nemotron-3.5-lightning:free` | chat, edit, apply, autocomplete | working — verified chat round-trip in the Continue panel |
| `oss-120b` | Groq `openai/gpt-oss-120b` | chat, edit, apply | reachable, but Groq free-tier caps 8000 tokens/minute and Continue's agent prompt is ~8.6k, so chat returns `RateLimitError` |
| `fast` | Groq `llama-3.1-8b-instant` | chat, edit, apply | broken upstream — Groq decommissioned the model, every call returns `404 model_not_found`. Defect belongs to the `lllm` stack config, not this one |
Practical consequence: select `free` in the model picker. Repairing `oss-120b`/`fast` means editing `dev/lllm/config/config.yaml` (a different stack) — not a cdsr change.
Workspace Trust gate: a fresh browser profile opens `/home/loca` in Restricted Mode, which silently disables every third-party extension — no Continue or Claude Code icon in the activity bar and no `Continue:` commands in the palette. Fix: Command Palette → `Workspaces: Manage Workspace Trust` → Trust. Symptom is indistinguishable from a failed install, so check trust before re-installing anything.
Claude Code is deliberately NOT wired to LiteLLM. The gateway does expose an Anthropic-compatible `/v1/messages` passthrough, but the reference probe model (`fast`) fails for the upstream reason above, so the extension stays on its interactive `/login` (browser OAuth) against an Anthropic account. Session state lives in the operator-wide `/home/loca/.claude/` and `/home/loca/.claude.json`, not in `data/user/`.
Cody (optional, only if enabled): sign in via the Cody panel; requires an Enterprise subscription.
`"extensions.autoUpdate": "off"` and `"update.mode": "manual"` are the deployed values in `data/user/User/settings.json` — code-server normalized the seeded `false` to the string `"off"` at first run, and `"off"` and `false` mean the same thing (auto-update disabled). Version drift only happens by updating `config/extensions.lock.json` and re-running the install script.

View File

@@ -0,0 +1,27 @@
---
title: 🛠️ Operations
description: Operations index for the code-server SSO-gated IDE stack — install/upgrade, auth/TLS, extensions, and verification procedures.
source: verified deployment on the loca operator host, 2026-08-19
reviewed: 2026-08-19
---
# 🛠️ Operations
Operations reference for the single-user code-server deployment at `cdsr.loca.zone`.
## Pages
- [[operations/install-upgrade]] — install or upgrade the upstream Debian package, service lifecycle, extension reinstall after Code version bumps, fallback path
- [[operations/auth-tls]] — Authelia access rule, nginx four-block topology, shared cert lineage, proof commands, rollback/debug order
- [[operations/extensions]] — pinned baseline table, optional Cody, explicit exclusions, first-run sign-in notes, install/audit scripts
- [[operations/verification]] — every smoke check with expected outputs: systemctl, loopback healthz, auth redirect, wiki public, Authelia compose, extension audit, browser flows, dev proxy, wiki build
## 🚨 Triage order when something is broken
1. `systemctl status cdsr --no-pager` — service active and running?
2. `curl -sf http://127.0.0.1:52224/healthz` — always HTTP 200 while the server is up. Body `{"status":"expired","lastHeartbeat":0}` is healthy-idle (no editor session connected); `{"status":"alive",...}` appears only while a browser session is connected. Only a non-200 or no response at all indicates failure
3. `sudo -n nginx -t` — nginx config clean (harmless flxr ssl_stapling warning is expected)?
4. Authelia rule present + container healthy:
- `grep -A2 'domain: "cdsr.loca.zone"' /home/loca/dev/services/authelia/config/configuration.yml`
- `cd /home/loca/dev/services/authelia && sudo -n docker compose ps`
- `cd /home/loca/dev/services/authelia && sudo -n docker compose logs --tail=50 authelia`

View File

@@ -0,0 +1,59 @@
---
title: ⬆️ Install & Upgrade
description: Install or upgrade the upstream code-server Debian package, service lifecycle commands, extension reinstall after Code version bumps, and fallback path.
source: verified deployment on the loca operator host, 2026-08-19
reviewed: 2026-08-19
---
# ⬆️ Install & Upgrade
Runtime is the upstream Debian package `code-server_4.133.0_amd64.deb` from `coder/code-server` v4.133.0. Binary at `/usr/bin/code-server`, libs at `/usr/lib/code-server`. The exact version is pinned in `config/runtime-version.txt`.
## First install (exact sequence as executed)
```bash
cd /home/loca/dev/code-server/tmp
curl -fsSL -O https://github.com/coder/code-server/releases/download/v4.133.0/code-server_4.133.0_amd64.deb
sudo -n dpkg -i code-server_4.133.0_amd64.deb
sudo -n install -m 644 -o root -g root /home/loca/dev/code-server/deploy/cdsr.service /etc/systemd/system/cdsr.service
sudo -n systemctl daemon-reload
sudo -n systemctl enable --now cdsr
```
## Upgrade sequence
```bash
cd /home/loca/dev/code-server/tmp
curl -fsSL -O https://github.com/coder/code-server/releases/download/v<NEW>/code-server_<NEW>_amd64.deb
sudo -n dpkg -i code-server_<NEW>_amd64.deb
echo "<NEW>" > /home/loca/dev/code-server/config/runtime-version.txt
cd /home/loca/dev/code-server && ./scripts/install-extensions.sh
cd /home/loca/dev/code-server && ./scripts/list-extension-versions.sh
sudo -n systemctl restart cdsr
```
A Code version bump can invalidate extension host compatibility — always re-run the extension install and audit after upgrading the runtime.
Order matters: extensions are installed and audited on disk before the service restart, so the restart is what loads the new extension versions into the extension host. Restarting first would leave any live browser session running the old extension host while the disk-based audit already reports green. If a session was open across the upgrade, reload the browser window (`Developer: Reload Window`) after the restart.
`config/runtime-version.txt` is `loca:loca` 644, so the version pin is written with a plain shell redirect — no `sudo` and no `tee` needed.
## Service lifecycle
- status: `systemctl status cdsr --no-pager`
- restart: `sudo -n systemctl restart cdsr`
- logs: `journalctl -u cdsr -n 50 --no-pager`
## Packaged unit stays disabled
`code-server@loca` (the default user-scope unit installed by the Debian package) is deliberately left disabled — verified by `systemctl is-enabled code-server@loca` -> `disabled`. Reason: it does not pass `--config /home/loca/dev/code-server/config/config.yaml`, so the editor's own user-data and extensions would land in the default locations (`~/.config/code-server`, `~/.local/share/code-server`) instead of `data/user` and `data/extensions`. That is still the wrong choice even though it is not an all-or-nothing isolation claim: heartbeat, server logs and extension-owned config already live outside the project regardless of `user-data-dir` — see [[architecture/runtime-topology#state-outside-the-project]].
## Fallback if the .deb ever fails
If a packaging mismatch prevents the Debian package from installing on this host, fall back to the upstream standalone Linux amd64 tarball:
1. Download and extract to `/home/loca/.local/lib/code-server-<version>`
2. Change only `ExecStart` in `deploy/cdsr.service` to point to the extracted binary: `/home/loca/.local/lib/code-server-<version>/bin/code-server`
3. Keep everything else identical: config, nginx, Authelia, wiki layout, service user, working directory, environment
This fallback preserves the same operational surface while bypassing the Debian package manager.

View File

@@ -0,0 +1,127 @@
---
title: ✅ Verification
description: "Numbered proof checklist for the deployed cdsr stack: service health, auth gate, wiki public, extensions audit, browser smoke, dev proxy, and wiki build."
source: verified deployment on the loca operator host, 2026-08-19
reviewed: 2026-08-19
---
# ✅ Verification checklist
Run in order. Each step must produce the expected output before proceeding.
## 1. systemd service
```bash
systemctl status cdsr --no-pager
```
Expected: `Active: active (running)` with `Main PID` present.
## 2. code-server loopback health
```bash
curl -sf http://127.0.0.1:52224/healthz
```
Expected (no editor session): JSON `{"status":"expired","lastHeartbeat":0}`
Expected (with browser session connected): JSON `{"status":"alive",...}`
```bash
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:52224/
```
Expected: `200`
## 3. app host requires Authelia
```bash
curl -sSI https://cdsr.loca.zone/
```
Expected: `HTTP/1.1 302` with `Location: https://auth.loca.zone/?rd=https%3A%2F%2Fcdsr.loca.zone%2F&rm=HEAD` (or equivalent redirect into Authelia).
## 4. wiki host is public
```bash
curl -sSI https://wiki.cdsr.loca.zone/
```
Expected: `HTTP/1.1 200` with `Content-Type: text/html` and no `Location` header pointing to `auth.loca.zone`.
## 5. Authelia config valid and container up
```bash
cd /home/loca/dev/services/authelia && sudo -n docker compose config
```
Expected: clean YAML output, no errors.
```bash
cd /home/loca/dev/services/authelia && sudo -n docker compose ps
```
Expected: `authelia` container `Up` (healthy).
## 6. extension baseline audit
```bash
cd /home/loca/dev/code-server && ./scripts/list-extension-versions.sh
```
Expected:
```
ok Continue.continue@2.1.0
ok Anthropic.claude-code@2.1.235
ok ms-python.python@2026.4.0
ok ms-python.debugpy@2026.6.0
ok ms-toolsai.jupyter@2025.9.1
ok ms-azuretools.vscode-containers@2.4.5
```
Exit code: `0`
## 7. browser smoke (manual)
1. Open `https://cdsr.loca.zone` in a browser.
2. Complete Authelia one-factor login at `auth.loca.zone`.
3. Confirm code-server editor shell loads (activity bar, file explorer, terminals).
4. Open Extensions view (Ctrl+Shift+X): verify `Continue` and `Claude Code` are installed and enabled.
5. Open integrated terminal (Ctrl+`): run `whoami` -> output `loca`.
## 8. dev-routing smoke (manual)
1. In the integrated terminal from step 7, run:
```bash
python3 -m http.server 52325 --bind 127.0.0.1
```
2. In the browser, open `https://cdsr.loca.zone/proxy/52325/`
3. Expected: directory listing or test page renders through code-server's built-in proxy.
## 9. wiki build smoke
```bash
cd /home/loca/dev/wikis && ./build.sh cdsr
```
Expected: build completes, emits `current -> dist-<timestamp>` symlink. A `git` warning about the vault not being a git repository is normal and expected.
Then open `https://wiki.cdsr.loca.zone/`:
- quick-start page renders
- internal wikilinks resolve (e.g. `[[operations/extensions]]`, `[[architecture/runtime-topology]]`)
- footer links point to `https://cdsr.loca.zone` and `https://loca.zone`
## Pre-publish wiki lint gate
Run before every `build.sh` invocation, so ahead of step 9 above. This is a gate, not a smoke test: a non-zero exit means nothing gets published.
```bash
python3 /home/loca/dev/wikis/lint-vault.py /home/loca/dev/code-server/q5vault
```
Expected: `11 pages, 0 findings, all 5 checks clean` and exit code `0`.
- `--quiet` suppresses the clean summary so only failures print, which is the form to use in a wrapper
- the vault may be named instead of pathed: `python3 /home/loca/dev/wikis/lint-vault.py cdsr` resolves `/home/loca/dev/wikis/cdsr/content`, the symlink into this vault
- why it exists: a frontmatter `description:` whose value held an unquoted colon-space made the Quartz note-properties transformer fail its YAML parse, and esbuild then died with a Go `all goroutines are asleep - deadlock!` panic instead of a readable error. One unquoted colon cost a whole build with no usable diagnostic
- it complements `scripts/validate-ssot.py`, which `build.sh` runs itself: that validator only gates wikis shipping a `wiki-ia.json` manifest, and this wiki ships none, so its `SSOT Validation passed` line is a vacuous pass here and this linter is the only real pre-publish gate on the vault
- check 1, frontmatter scalars: unquoted values containing a colon-space, starting with `#`, `@`, `*`, `&`, `!`, `%` or a backtick, ending in a colon, carrying an inline ` #` comment that would silently truncate the value, or holding an unterminated quote. Reported as `file:line` plus the offending key
- check 2, wikilinks in tables: an unescaped `|` inside `[[...]]` on a table row splits the cell, and an alias containing `/` is truncated at the slash when rendered
- check 3, dead wikilinks: every target must resolve to a real `.md` in the vault, tried vault-root-relative first and then relative to the linking page, tolerating a leading `./` and an optional `#anchor`
- check 4, house style: no markdown bold anywhere
- check 5, page shape: no second H1 in one file, and no page without a frontmatter block
- checks 2 to 5 ignore fenced blocks and inline code spans, because nothing in those regions renders as markdown, so the example wikilinks in step 9 above are not treated as links
- standard library only, so it runs on any host with `python3` and needs no pyyaml
---
Note: the git-repository warning from `build.sh` is normal -- the vault is a plain directory, not a git repo.

View File

@@ -0,0 +1,69 @@
---
title: 📂 Config & Paths Reference
description: Exhaustive table of every config file, state directory, system path, and port used by the code-server deployment, with purpose and owner
source: verified deployment on the loca operator host, 2026-08-19
reviewed: 2026-08-19
---
# 📂 Config & Paths Reference
## Path & Port Table
| Path / Port | Purpose | Owner / Permissions | Notes |
| --- | --- | --- | --- |
| `config/config.yaml` | code-server runtime config | `loca:loca` 644 | Effective config shown below |
| `config/extensions.lock.json` | pinned extension versions + VSIX metadata | `loca:loca` 644 | Baseline + optional entries |
| `config/runtime-version.txt` | upstream code-server version pin | `loca:loca` 644 | Content: `4.133.0` |
| `scripts/install-extensions.sh` | installs baseline from lock file | `loca:loca` 755 | Reads `config/extensions.lock.json`, verifies sha256, `--include-optional` for optional entries |
| `scripts/list-extension-versions.sh` | audits installed vs lock file | `loca:loca` 755 | Exits non-zero on MISSING/MISMATCH |
| `deploy/cdsr.service` | systemd unit source | `loca:loca` 644 | Installed to `/etc/systemd/system/cdsr.service` |
| `deploy/cdsr.nginx.conf` | nginx site config source | `loca:loca` 644 | Installed to `/etc/nginx/sites-available/cdsr.loca.zone` |
| `data/user/` | code-server user data dir | `loca:loca` 755 | Set via `user-data-dir` |
| `data/user/User/settings.json` | VS Code settings (telemetry off, auto-update off) | `loca:loca` 644 | Seeded at deploy |
| `data/extensions/` | installed extensions dir | `loca:loca` 755 | Set via `extensions-dir` |
| `tmp/extensions/` | VSIX download cache | `loca:loca` 755 | Filenames `<id>-<version>@<platform>.vsix` |
| `q5vault/` | Quartz wiki vault (real content) | `loca:loca` 755 | Symlinked as `/home/loca/dev/wikis/cdsr/content` |
| `quartz.config.cdsr.yaml` | Quartz config for this wiki | `loca:loca` 644 | Symlinked as `/home/loca/dev/wikis/cdsr/quartz.config.yaml` |
| `/etc/systemd/system/cdsr.service` | installed systemd unit | `root:root` 644 | Requires `sudo -n install` for edits; enable/start via `systemctl` |
| `/etc/nginx/sites-available/cdsr.loca.zone` | installed nginx site | `root:root` 644 | Requires `sudo -n install` for edits; symlinked into `sites-enabled` |
| `/etc/nginx/sites-enabled/cdsr.loca.zone` | symlink to sites-available | `root:root` 755 | Created by `ln -sf` |
| `/etc/nginx/snippets/security-headers-app.conf` | security headers for app host | `root:root` 644 | Included in app 443 block |
| `/etc/nginx/snippets/security-headers.conf` | security headers for wiki host | `root:root` 644 | Included in wiki 443 block |
| `/etc/nginx/snippets/authelia-location.conf` | Authelia location config | `root:root` 644 | Included in app 443 block |
| `/etc/nginx/snippets/authelia-authrequest.conf` | auth_request to Authelia | `root:root` 644 | Protects app `location /` |
| `/etc/letsencrypt/live/cdsr.loca.zone/` | TLS cert lineage | `root:root` 700 dir, 600 files | `fullchain.pem`, `privkey.pem`; expires 2026-11-17 |
| `/home/loca/dev/services/authelia/config/configuration.yml` | Authelia config | `root:root` 644 | Requires `sudo -n install` after staging in `/tmp`; rule `cdsr.loca.zone` = `one_factor` |
| `/home/loca/dev/wikis/cdsr/content` | symlink → `q5vault/` | `loca:loca` symlink | Wiki build source |
| `/home/loca/dev/wikis/cdsr/quartz.config.yaml` | symlink → `quartz.config.cdsr.yaml` | `loca:loca` symlink | Wiki build config |
| `/home/loca/dev/wikis/cdsr/current` | symlink → latest `dist-*` build | `loca:loca` symlink | Nginx `root` points here |
| `/usr/bin/code-server` | upstream binary (Debian package) | `root:root` 755 | Installed via `dpkg -i` from GitHub release v4.133.0 |
| `/usr/lib/code-server` | upstream libs (Node, extensions host) | `root:root` 755 | Package-managed, do not modify |
## Loopback Port & Range
- `127.0.0.1:52224` — code-server bind address (chosen inside mandatory 51000-53000 loopback range for loca.zone app backends)
- Health endpoint: `http://127.0.0.1:52224/healthz` — always HTTP 200 while the server is up; body is `{"status":"expired","lastHeartbeat":0}` when no editor session is connected (healthy-idle) and `{"status":"alive",...}` while a browser session is connected. Only a non-200 or no response indicates failure; HTTP 200 from `/` also proves liveness
## Effective `config/config.yaml`
```yaml
bind-addr: 127.0.0.1:52224
auth: none
cert: false
app-name: Code Server · loca.zone
user-data-dir: /home/loca/dev/code-server/data/user
extensions-dir: /home/loca/dev/code-server/data/extensions
disable-telemetry: true
```
## Root-Owned Files Requiring `sudo -n install`
- `/etc/systemd/system/cdsr.service`
- `/etc/nginx/sites-available/cdsr.loca.zone`
- `/etc/nginx/snippets/security-headers-app.conf`
- `/etc/nginx/snippets/security-headers.conf`
- `/etc/nginx/snippets/authelia-location.conf`
- `/etc/nginx/snippets/authelia-authrequest.conf`
- `/home/loca/dev/services/authelia/config/configuration.yml`
Edit workflow: stage in `/tmp`, then `sudo -n install -m 644 -o root -g root /tmp/<file> /target/path`.

View File

@@ -0,0 +1,93 @@
---
title: 🔀 Dev Server Routing
description: code-server built-in proxy routes, framework caveats, and why pretty per-port subdomains are not configured here
source: verified deployment on the loca operator host, 2026-08-19
reviewed: 2026-08-19
---
# 🔀 Dev Server Routing
## Why No Pretty Subdomains
Pretty per-port subdomain proxying (`proxy-domain`) is intentionally not configured because:
- DNS is not the blocker — multi-label wildcards do resolve on this host; `3000.cdsr.loca.zone` and deeper names all answer with the host address
- TLS certificate issuance is the blocker — the `cdsr.loca.zone` lineage covers exactly two SANs: `cdsr.loca.zone` and `wiki.cdsr.loca.zone`
- A `*.cdsr.loca.zone` certificate would require DNS-01 validation with DNS-provider API credentials, which are not configured on this host
- HTTP-01 via webroot (the method used for every lineage here) cannot issue wildcard certificates at all
- Therefore per-port hostnames would serve a name mismatch to the browser; code-server's built-in `/proxy/<port>/` and `/absproxy/<port>/` routes remain the supported routes and need no new certificate
See upstream: [Accessing web services — Using a subdomain](https://coder.com/docs/code-server/guide#using-a-subdomain) about requiring a DNS entry per port and wildcard TLS support.
## Default Route: `/proxy/<port>/`
The default subpath proxy strips the `/proxy/<port>` prefix before forwarding to the dev server.
Worked example:
```bash
# In code-server integrated terminal (binds 127.0.0.1 only)
python3 -m http.server 52325 --bind 127.0.0.1
```
Then open:
```
https://cdsr.loca.zone/proxy/52325/
```
- Trailing slash matters, always use it
- Dev server must bind `127.0.0.1`, not `0.0.0.0`
- Port must not collide with code-server loopback port `52224`
- Ad-hoc dev-server ports must avoid every port in the AGENTS.md `port_convention` registry (52222 pwiz, 52223 reserved for pwiz, 52224 cdsr, 52226 code) and must be checked with `ss -ltn` before binding — an ad-hoc listener inside 51000-53000 can shadow a registered backend and block that service's restart
Upstream: [Using a subpath](https://coder.com/docs/code-server/guide#using-a-subpath)
## Path-Preserving Route: `/absproxy/<port>/`
Use `/absproxy/<port>/` when the framework needs the request path preserved and not stripped.
| Route | Path sent to dev server |
| --- | --- |
| `/proxy/3000/app/path` | `/app/path` |
| `/absproxy/3000/app/path` | `/absproxy/3000/app/path` |
Upstream: [Stripping `/proxy/<port>` from the request path](https://coder.com/docs/code-server/guide#stripping-proxyport-from-the-request-path)
## Framework Caveat Table
| Framework | Route | Required config |
| --- | --- | --- |
| React / create-react-app | `/absproxy/<port>/` | `PUBLIC_URL=/absproxy/<port>` and `WDS_SOCKET_PATH=$PUBLIC_URL/sockjs-node` |
| Vue (Vue CLI) | `/absproxy/<port>/` | `publicPath: "/absproxy/<port>"` and `devServer.sockPath: "sockjs-node"` |
| Angular | `/absproxy/<port>/` | `<base href="./">` in `src/index.html` and `--serve-path /absproxy/<port>` in `ng serve` |
| Svelte (SvelteKit) | `/absproxy/<port>/` | `kit.paths.base: "/absproxy/<port>"` in `svelte.config.js` |
Notes:
- React requirement source: [Proxying to create a React app](https://coder.com/docs/code-server/guide#proxying-to-create-a-react-app)
- Vue requirement source: [Proxying to a Vue app](https://coder.com/docs/code-server/guide#proxying-to-a-vue-app)
- Angular requirement source: [Proxying to an Angular app](https://coder.com/docs/code-server/guide#proxying-to-an-angular-app)
- Svelte requirement source: [Proxying to a Svelte app](https://coder.com/docs/code-server/guide#proxying-to-a-svelte-app)
- HMR note: websocket-driven HMR needs Nginx headers already configured in the app block (`proxy_read_timeout 86400`, `Upgrade $http_upgrade`, `Connection $connection_upgrade`)
## Troubleshooting Checklist
- Dev server binds to `0.0.0.0` only; set `--bind 127.0.0.1` or `--host 127.0.0.1`
- Missing trailing slash (`/proxy/52325` versus `/proxy/52325/`)
- Proxy port equals `52224`
- Assets load from `/` and not the proxied prefix, indicating a base path mismatch
- HMR fails to connect, confirm websocket forwarding and timeout headers in Nginx
- CORS/preflight issues on custom auth chains, see `--skip-auth-preflight` if auth is enabled
## Source URLs (read during this task)
- https://coder.com/docs/code-server/guide
- https://coder.com/docs/code-server/guide#using-a-subdomain
- https://coder.com/docs/code-server/guide#using-a-subpath
- https://coder.com/docs/code-server/guide#stripping-proxyport-from-the-request-path
- https://coder.com/docs/code-server/guide#proxying-to-create-a-react-app
- https://coder.com/docs/code-server/guide#proxying-to-a-vue-app
- https://coder.com/docs/code-server/guide#proxying-to-an-angular-app
- https://coder.com/docs/code-server/guide#proxying-to-a-svelte-app
- https://coder.com/docs/code-server/guide#preflight-requests

View File

@@ -0,0 +1,18 @@
---
title: 📚 Reference Index
description: Landing page for reference documentation linking to config paths and dev routing pages
source: verified deployment on the loca operator host, 2026-08-19
reviewed: 2026-08-19
---
# 📚 Reference Index
Quick links to reference pages:
- [[reference/config-paths]] — exhaustive table of every config file, state directory, system path, and port used by this deployment, with owner and purpose
- [[reference/dev-routing]] — code-server built-in proxy routes (`/proxy/<port>/` and `/absproxy/<port>/`), framework caveats, and why pretty subdomains are not used here
Back links:
- [[index]] — quick-start cheat sheet
- [[operations/index]] — operations landing page