import { dirname, join } from 'node:path'; import { DATA_PATH } from '../data-path'; // Where the app store puts the containers it provisions. // // ── The install layout ── // // A machine that runs Officer is meant to look like this, whether the owner is seasoned or not: // // ~/officerdev/ // platform/ the app // data/ DATA_PATH — managed homes, attachments, job logs // dockers/ services the app store provisioned <- this file // capabilities/ the file-based item store // // One root, everything under it, nothing scattered. `OFFICER_ROOT` is derived from `DATA_PATH` rather // than configured separately, because a second environment variable that must agree with the first is a // second thing to get wrong — and on a correct install `data/` is always a direct child of the root. // // (This development machine predates the convention and has it inverted: the whole project sits inside // `~/dockers/officer.dev/`, so the root derives to `officer.dev` and the app store's directory would be // `~/dockers/officer.dev/dockers`. Which is ugly, and correct — it is still isolated, still under one // root, and still not mixed in with anything else. New installs get the clean shape.) // // ── Why this is not `~/dockers` ── // // That is where a seasoned user already keeps their own estate — 47 services on this machine alone. Two // reasons to stay out of it: // // 1. **Isolation.** Containers the app store created and containers the user manages must be // distinguishable without inspecting them. A separate root makes that structural rather than a // naming convention we would have to enforce and they could break. // 2. **We never reason about someone else's compose files.** The app store does not scan, adopt or // modify anything outside its own directory. "I already have one of these" is answered by the user // giving a URL (`mode: 'existing'`), never by us finding a directory and guessing it is theirs. // // So this directory is exclusively ours to write, and everything in it was put there by an install. /** * The install root — the parent of `data/`. On a conventional install, `~/officerdev`. * * Derived, not configured: see above. */ export const OFFICER_ROOT = dirname(DATA_PATH); /** Where provisioned services live, one directory each. Created on first install, not at boot. */ export const DOCKERS_DIR = join(OFFICER_ROOT, 'dockers'); /** * This service's own directory: `/dockers//`, holding `docker-compose.yaml` and — because the * templates use relative bind mounts rather than named volumes — its data and configuration too. * * That is the convention the owner already uses everywhere: `./data`, `./database`, `./storage` beside * the compose file, so both the app and a human can see exactly what a service is keeping and where. * A named volume hides it behind `docker volume inspect`, which is the opposite of the point. */ export const serviceDir = (sidecarId: string): string => join(DOCKERS_DIR, sidecarId); /** The compose file the installer renders and `docker compose` is run against. */ export const composeFile = (sidecarId: string): string => join(serviceDir(sidecarId), 'docker-compose.yaml');