standardize laser everything migration names

This commit is contained in:
makearmy 2026-07-10 20:38:40 -04:00
parent 27fd4f4ae3
commit 0e3d5ea5ca
5 changed files with 197 additions and 122 deletions

View file

@ -1,4 +1,4 @@
# Payload migration architecture proposal # Laser Everything database transition architecture
Status: pre-implementation decision record. No production changes are authorized by this document. Status: pre-implementation decision record. No production changes are authorized by this document.
@ -31,7 +31,7 @@ The migration branch initially started from `main` at `0a7ee5f`. A tree and hist
- Those commits repair production registration/authentication behavior and add the verified Directus deployment contract, policies, flows, exports, and schema snapshot. - Those commits repair production registration/authentication behavior and add the verified Directus deployment contract, policies, flows, exports, and schema snapshot.
- The application was not replaced or reduced in those commits. - The application was not replaced or reduced in those commits.
Conclusion: `main` is a complete application tree, but `4884b5d` is the correct and more complete pre-utilities production baseline. The dedicated `migration/payload` branch has therefore been fast-forwarded to `4884b5d`. The temporary utilities branch is not an ancestor of the migration work. Conclusion: `main` is a complete application tree, but `4884b5d` is the correct and more complete pre-utilities production baseline. The dedicated `migration/le-app-database` branch has therefore been fast-forwarded to `4884b5d`. The temporary utilities branch is not an ancestor of the migration work.
## Service boundary ## Service boundary
@ -141,7 +141,7 @@ CI builds the frontend, Payload, BGBye, and proxy images once from reviewed comm
The permanent production checkout and deployment root is `/var/www/lasereverything.net.db`. It contains the complete versioned stack: frontend, Payload, BGBye, Compose definitions, migrations, deployment scripts, internal gateway contract, health checks, and validation tooling. No production checkout or application deployment may run from `/srv`, `/opt`, a Codex workspace, a temporary worktree, or another project root. Operational Docker layers, declared database/media volumes, and server-wide proxy files may live elsewhere, but the deployment rooted at the canonical production checkout declares and controls their contracts. The permanent production checkout and deployment root is `/var/www/lasereverything.net.db`. It contains the complete versioned stack: frontend, Payload, BGBye, Compose definitions, migrations, deployment scripts, internal gateway contract, health checks, and validation tooling. No production checkout or application deployment may run from `/srv`, `/opt`, a Codex workspace, a temporary worktree, or another project root. Operational Docker layers, declared database/media volumes, and server-wide proxy files may live elsewhere, but the deployment rooted at the canonical production checkout declares and controls their contracts.
The sole persistent nonproduction project root is `/srv/le-payload-dev`, with the checkout at `/srv/le-payload-dev/lasereverything.net.db`. The dedicated account design does not retain the current nested `.worktrees/payload-migration` layout. Temporary worktrees may be created under the canonical nonproduction root for a specific documented task and must be removed when that task ends. The sole persistent nonproduction project root is `/srv/le-app-codex`, with the checkout at `/srv/le-app-codex/lasereverything.net.db`. The dedicated account design does not retain the current nested `.worktrees/payload-migration` layout. Temporary worktrees may be created under the canonical nonproduction root for a specific documented task and must be removed when that task ends.
Production database schema changes use committed Payload migrations as an explicit release job before application rollout. Imports are migration-only jobs and are not part of ordinary application startup. Promotion requires recorded image digests, migration status, validation results, rollback compatibility, and health checks. Production database schema changes use committed Payload migrations as an explicit release job before application rollout. Imports are migration-only jobs and are not part of ordinary application startup. Promotion requires recorded image digests, migration status, validation results, rollback compatibility, and health checks.

View file

@ -140,7 +140,7 @@ Local HTTPS is required to test secure cookies. A repository-controlled developm
## Rootless Codex-controlled runtime ## Rootless Codex-controlled runtime
Required target: a dedicated rootless Docker daemon owned by the separate `le_payload_dev` account. Future Codex migration sessions must execute as that account (preferred) or through a narrowly scoped account transition; the daemon socket is not shared broadly. Rootless Podman is an acceptable fallback only after Compose/profile behavior is proven; Docker is preferred because the repository and production already use Compose. Required target: a dedicated rootless Docker daemon owned by the separate `le_app_codex` account. Future Codex migration sessions must execute as that account (preferred) or through a narrowly scoped account transition; the daemon socket is not shared broadly. Rootless Podman is an acceptable fallback only after Compose/profile behavior is proven; Docker is preferred because the repository and production already use Compose.
Codex must be able to build images, create/remove only its project containers/networks/volumes, inspect their logs, reset disposable PostgreSQL, run finite jobs, and execute integration/browser tests. It must not access `/var/run/docker.sock`, the production daemon/context, production containers/volumes/networks, `/var/www`, or `/srv/directus-archive`. Codex must be able to build images, create/remove only its project containers/networks/volumes, inspect their logs, reset disposable PostgreSQL, run finite jobs, and execute integration/browser tests. It must not access `/var/run/docker.sock`, the production daemon/context, production containers/volumes/networks, `/var/www`, or `/srv/directus-archive`.
@ -154,9 +154,9 @@ Current host observation:
Therefore a `sol6_vi`-owned daemon is rejected: it could bind-mount those readable production secrets. Milestone 0 requires the isolated account and host changes described in `host-runtime-plan.md`, and work must stop before performing them. An administrator must approve/provision: Therefore a `sol6_vi`-owned daemon is rejected: it could bind-mount those readable production secrets. Milestone 0 requires the isolated account and host changes described in `host-runtime-plan.md`, and work must stop before performing them. An administrator must approve/provision:
1. dedicated `le_payload_dev` identity and restricted filesystem ACLs; 1. dedicated `le_app_codex` identity and restricted filesystem ACLs;
2. rootless prerequisites (`newuidmap`/`newgidmap`, subordinate UID/GID ranges, rootless networking and overlay helpers as required by the OS); 2. rootless prerequisites (`newuidmap`/`newgidmap`, subordinate UID/GID ranges, rootless networking and overlay helpers as required by the OS);
3. rootless Docker daemon installation for `le_payload_dev` using its dedicated data root and named context; 3. rootless Docker daemon installation for `le_app_codex` using its dedicated data root and named context;
4. optional user-service lingering if stacks must survive logout; 4. optional user-service lingering if stacks must survive logout;
5. deterministic CPU BGBye first; optional GPU access only after core acceptance; 5. deterministic CPU BGBye first; optional GPU access only after core acceptance;
6. user/browser-profile-scoped development CA trust; 6. user/browser-profile-scoped development CA trust;
@ -231,7 +231,7 @@ Build verification inspects image histories and filesystems for forbidden names
│ ├── contracts/ │ ├── contracts/
│ ├── e2e/ │ ├── e2e/
│ └── fixtures/files/ │ └── fixtures/files/
├── docs/payload-migration/ ├── docs/le-app-database-migration/
├── directus/ # retained legacy evidence during migration ├── directus/ # retained legacy evidence during migration
├── .nvmrc / .node-version ├── .nvmrc / .node-version
└── Dockerfiles remain beside each build context └── Dockerfiles remain beside each build context
@ -239,7 +239,7 @@ Build verification inspects image histories and filesystems for forbidden names
The current `app`, `bgbye`, and `dashboard` directories will be moved only in a dedicated mechanical commit after build-context and path references are covered by tests. Milestone 0 may initially implement the target logical boundaries without a risky all-at-once directory move. The current `app`, `bgbye`, and `dashboard` directories will be moved only in a dedicated mechanical commit after build-context and path references are covered by tests. Milestone 0 may initially implement the target logical boundaries without a risky all-at-once directory move.
The repository tree above exists at exactly one persistent nonproduction checkout, `/srv/le-payload-dev/lasereverything.net.db`, and at the canonical production root, `/var/www/lasereverything.net.db`. Runtime data is never placed inside either Git checkout unless it is a deliberately versioned non-secret fixture. The complete canonical host directory policy and transitional consolidation are defined in `host-runtime-plan.md`. The repository tree above exists at exactly one persistent nonproduction checkout, `/srv/le-app-codex/lasereverything.net.db`, and at the canonical production root, `/var/www/lasereverything.net.db`. Runtime data is never placed inside either Git checkout unless it is a deliberately versioned non-secret fixture. The complete canonical host directory policy and transitional consolidation are defined in `host-runtime-plan.md`.
## End-to-end test matrix ## End-to-end test matrix

View file

@ -26,57 +26,90 @@ Conclusion: rootless Docker owned by `sol6_vi` would protect the production daem
| Model | Advantages | Risks / cost | Decision | | Model | Advantages | Risks / cost | Decision |
|---|---|---|---| |---|---|---|---|
| `sol6_vi` owns rootless Docker | Minimal provisioning; current workspace already writable | Can bind-mount world-readable production secrets and application files; mixes general Codex work with container authority | Rejected on this host | | `sol6_vi` owns rootless Docker | Minimal provisioning; current workspace already writable | Can bind-mount world-readable production secrets and application files; mixes general Codex work with container authority | Rejected on this host |
| dedicated `le_payload_dev` | Daemon owner has an explicit filesystem allow-list; project storage and cleanup are attributable; production paths can be proven inaccessible | Requires account, ACL, subordinate IDs, separate Git credentials/session, storage, and user service | **Recommended** | | dedicated `le_app_codex` | Daemon owner has an explicit filesystem allow-list; project storage and cleanup are attributable; production paths can be proven inaccessible | Requires account, ACL, subordinate IDs, separate Git credentials/session, storage, and user service | **Recommended** |
Future autonomous migration work should run in a Codex session whose OS identity is `le_payload_dev`. Do not grant `sol6_vi` generic access to the development daemon socket as a convenience; that weakens accountability. If orchestration must be initiated from another account, use a narrowly audited command wrapper or SSH into `le_payload_dev`, not a shared Docker group and not TCP exposure of the daemon. Future autonomous migration work should run in a Codex session whose OS identity is `le_app_codex`. Do not grant `sol6_vi` generic access to the development daemon socket as a convenience; that weakens accountability. If orchestration must be initiated from another account, use a narrowly audited command wrapper or SSH into `le_app_codex`, not a shared Docker group and not TCP exposure of the daemon.
## Exact proposed identity and filesystem model ## Exact proposed identity and filesystem model
Proposed fixed identity: Proposed fixed identity:
- user and primary group: `le_payload_dev`; - user and primary group: `le_app_codex`;
- UID/GID: `1200` (verified unused at audit time; recheck immediately before creation); - UID/GID: `1200` (verified unused at audit time; recheck immediately before creation);
- shell/home: `/bin/bash`, `/home/le_payload_dev`; - shell/home: `/bin/bash`, `/srv/le-app-codex/home`;
- no supplementary production groups, no `docker` group, no sudo/wheel membership; - no supplementary production groups, no `docker` group, no sudo/wheel membership;
- subordinate UID and GID range: `165536:65536`, non-overlapping with the observed `sol6_ii:100000:65536` allocation; - subordinate UID and GID range: `165536:65536`, non-overlapping with the observed `sol6_ii:100000:65536` allocation;
- dedicated project root: Btrfs subvolume `/srv/le-payload-dev`, owner `root:le_payload_dev`, mode `0750`; - dedicated project root: Btrfs subvolume `/srv/le-app-codex`, owner `root:le_app_codex`, mode `0750`;
- sole checkout: `/srv/le-payload-dev/lasereverything.net.db`; - sole checkout: `/srv/le-app-codex/lasereverything.net.db`;
- authoritative scrubbed input: `/srv/le-payload-dev/source`, root-owned and read-only to the development group; - authoritative scrubbed input: `/srv/le-app-codex/database-source-readonly`, root-owned and read-only to the development group;
- nonproduction credentials only: `/srv/le-payload-dev/secrets`; - nonproduction credentials only: `/srv/le-app-codex/nonproduction-secrets`;
- daemon data root: `/srv/le-payload-dev/runtime/docker`; - daemon data root: `/srv/le-app-codex/container-runtime/docker`;
- runtime socket: `/run/user/1200/docker.sock`; - runtime socket: `/run/user/1200/docker.sock`;
- Docker client/config: `/home/le_payload_dev/.docker` and `/home/le_payload_dev/.config/docker`; - Docker client/config: `/srv/le-app-codex/home/.docker` and `/srv/le-app-codex/home/.config/docker`;
- Compose project prefix: `le_payload_dev`, enforced by wrapper preflight. - rootless Docker context: `le-app-codex`;
- default nonproduction Compose project: `le-app-testing`, enforced by wrapper preflight.
Verified candidate availability at this planning checkpoint:
- neither account nor group name `le_app_codex` exists;
- UID `1200` is unused in `/etc/passwd` and GID `1200` is unused in `/etc/group`;
- no passwd primary UID/GID or group GID collision exists at `1200`;
- `/etc/subuid` and `/etc/subgid` contain one observed allocation, `100000165535`, owned by the historical `sol6_ii` entry;
- candidate range `165536231071` does not overlap any entry in either subordinate-ID file;
- `/srv/le-app-codex` does not exist.
Therefore the current exact recommendation is UID/GID `1200` and subordinate range `165536:65536`, subject to rerunning the documented collision checks immediately before provisioning. If any check produces output or a non-free result, stop and select new IDs; do not force reuse.
## Canonical naming replacements
| Superseded proposal | Canonical replacement |
|---|---|
| account/group `le_payload_dev` | `le_app_codex` |
| nonproduction root `/srv/le-payload-dev` | `/srv/le-app-codex` |
| checkout `/srv/le-payload-dev/lasereverything.net.db` | `/srv/le-app-codex/lasereverything.net.db` |
| source directory `source` | `database-source-readonly` |
| secrets directory `secrets` | `nonproduction-secrets` |
| runtime directory `runtime` | `container-runtime` |
| disposable-data directory `data` | `testing-data` |
| artifact directory `artifacts` | `build-artifacts` |
| Docker context `le-payload-dev` | `le-app-codex` |
| Compose project `le_payload_dev` | `le-app-testing` |
| branch `migration/payload` | `migration/le-app-database` |
| documentation `docs/payload-migration` | `docs/le-app-database-migration` |
`payload` remains valid only for the actual Payload CMS service, application directory, packages, configuration, and Payload-specific migrations. Historical inventory names such as `le-payload-pg` and `.worktrees/payload-migration` remain unchanged because they identify real transitional resources.
Canonical nonproduction tree: Canonical nonproduction tree:
```text ```text
/srv/le-payload-dev/ /srv/le-app-codex/
├── lasereverything.net.db/ # sole persistent nonproduction Git checkout ├── home/ # dedicated account home and user configuration
├── source/ # root-owned scrubbed authoritative input ├── lasereverything.net.db/ # sole persistent nonproduction Git checkout
├── secrets/ # nonproduction credentials only ├── database-source-readonly/ # root-owned scrubbed authoritative input
├── runtime/ # rootless Docker data/configuration support ├── nonproduction-secrets/ # testing/development credentials only
├── data/ # disposable PostgreSQL, media, clones, caches, temp ├── container-runtime/ # rootless Docker images, layers and configuration
└── artifacts/ # migration reports, logs, browser evidence ├── testing-data/ # disposable databases, media, models and caches
└── build-artifacts/ # reports, logs and browser/test evidence
``` ```
Ownership and permissions: Ownership and permissions:
| Path | Owner:group | Mode/policy | | Path | Owner:group | Mode/policy |
|---|---|---| |---|---|---|
| `/srv/le-payload-dev` | `root:le_payload_dev` | `0750`; prevents unrelated users and prevents project-root restructuring by the development account | | `/srv/le-app-codex` | `root:le_app_codex` | `0750`; prevents unrelated users and prevents project-root restructuring by the development account |
| `lasereverything.net.db` | `le_payload_dev:le_payload_dev` | directories `0750`, normal Git files subject to repository modes; sole checkout | | `home` | `le_app_codex:le_app_codex` | `0700`; account, Docker client, user systemd, NSS/browser configuration |
| `source` | `root:le_payload_dev` | directories `0550`, files `0440`; account and daemon may read but cannot alter; no writable symlink target | | `lasereverything.net.db` | `le_app_codex:le_app_codex` | directories `0750`, normal Git files subject to repository modes; sole checkout |
| `secrets` | `root:le_payload_dev` | directory `0750`, provisioned files `0640`; nonproduction values only; optional `generated/` is `0700 le_payload_dev` for ephemeral fixture secrets | | `database-source-readonly` | `root:le_app_codex` | directories `0550`, files `0440`; account and daemon may read but cannot alter; no writable symlink target |
| `runtime` | `le_payload_dev:le_payload_dev` | `0700`; rootless Docker layers/configuration only | | `nonproduction-secrets` | `root:le_app_codex` | directory `0750`, provisioned files `0640`; nonproduction values only; optional `generated/` is `0700 le_app_codex` for ephemeral fixture secrets |
| `data` | `le_payload_dev:le_payload_dev` | `0700`; child ownership may use rootless subordinate IDs | | `container-runtime` | `le_app_codex:le_app_codex` | `0700`; rootless Docker images, layers and configuration only |
| `artifacts` | `le_payload_dev:le_payload_dev` | `0700`; TTL cleanup; snapshot artifacts treated as sensitive | | `testing-data` | `le_app_codex:le_app_codex` | `0700`; child ownership may use rootless subordinate IDs |
| `build-artifacts` | `le_app_codex:le_app_codex` | `0700`; TTL cleanup; snapshot artifacts treated as sensitive |
Only the dedicated account receives: Only the dedicated account receives:
- read/write access to its workspace and runtime/storage subtree; - read/write access to its workspace and runtime/storage subtree;
- read/traverse ACL on the frozen scrubbed source only; - group read/traverse access to canonical root-owned `database-source-readonly`;
- read ACL on `/srv/codex-secrets/le-payload-migration.env` only; - group read access to specifically provisioned files under canonical `nonproduction-secrets`;
- a separate nonproduction Git credential/deploy key with access limited to the application repository. - a separate nonproduction Git credential/deploy key with access limited to the application repository.
It receives no ACL or group membership for `/var/www`, production database paths, production sockets/volumes, or `/srv/directus-archive`. It receives no ACL or group membership for `/var/www`, production database paths, production sockets/volumes, or `/srv/directus-archive`.
@ -86,10 +119,16 @@ It receives no ACL or group membership for `/var/www`, production database paths
These commands are a reviewable proposal for an administrator on Garuda/Arch Linux. Recheck UID/GID and subordinate ranges first. Do not paste secrets into the shell history. These commands are a reviewable proposal for an administrator on Garuda/Arch Linux. Recheck UID/GID and subordinate ranges first. Do not paste secrets into the shell history.
```bash ```bash
# 1. Recheck proposed IDs/ranges are unused. # 1. Recheck names, IDs, and ranges across all four account databases.
getent passwd le_app_codex
getent group le_app_codex
getent passwd 1200 getent passwd 1200
getent group 1200 getent group 1200
grep -E '^(le_payload_dev|[^:]+:165536:65536)$' /etc/subuid /etc/subgid awk -F: '$3 == 1200 || $4 == 1200 { print; found=1 } END { exit found ? 1 : 0 }' /etc/passwd
awk -F: '$3 == 1200 { print; found=1 } END { exit found ? 1 : 0 }' /etc/group
awk -F: 'BEGIN { a=165536; b=231071 }
{ x=$2; y=$2+$3-1; if (a<=y && x<=b) { print FILENAME ":" $0; found=1 } }
END { exit found ? 1 : 0 }' /etc/subuid /etc/subgid
# 2. Install rootless prerequisites. docker-rootless-extras is an Arch AUR # 2. Install rootless prerequisites. docker-rootless-extras is an Arch AUR
# package and must be built/reviewed under the administrator's normal AUR policy. # package and must be built/reviewed under the administrator's normal AUR policy.
@ -97,45 +136,49 @@ sudo pacman -S --needed rootlesskit slirp4netns fuse-overlayfs shadow
# Then install the reviewed docker-rootless-extras AUR package by the host's # Then install the reviewed docker-rootless-extras AUR package by the host's
# approved AUR procedure; do not curl a remote install script into a shell. # approved AUR procedure; do not curl a remote install script into a shell.
# 3. Create the isolated identity. # 3. Create the isolated primary group and project subvolume.
sudo groupadd --gid 1200 le_payload_dev sudo groupadd --gid 1200 le_app_codex
sudo useradd --uid 1200 --gid le_payload_dev --create-home \ sudo btrfs subvolume create /srv/le-app-codex
--home-dir /home/le_payload_dev --shell /bin/bash le_payload_dev sudo chown root:le_app_codex /srv/le-app-codex
sudo usermod --add-subuids 165536-231071 --add-subgids 165536-231071 le_payload_dev sudo chmod 0750 /srv/le-app-codex
# 4. Create a quota-capable project subvolume and ownership boundary. # 4. Create the account with its canonical home inside the project root.
sudo btrfs subvolume create /srv/le-payload-dev sudo useradd --uid 1200 --gid le_app_codex --create-home \
sudo chown 1200:1200 /srv/le-payload-dev --home-dir /srv/le-app-codex/home --shell /bin/bash le_app_codex
sudo chmod 0700 /srv/le-payload-dev sudo usermod --add-subuids 165536-231071 --add-subgids 165536-231071 le_app_codex
sudo -u le_payload_dev mkdir -p \
/srv/le-payload-dev/lasereverything.net.db \ # 5. Create the canonical children; no sibling workspace is created.
/srv/le-payload-dev/source \ sudo mkdir -p \
/srv/le-payload-dev/secrets/generated \ /srv/le-app-codex/lasereverything.net.db \
/srv/le-payload-dev/runtime/docker \ /srv/le-app-codex/database-source-readonly \
/srv/le-payload-dev/data/{postgres,media,legacy-uploads,bgbye-models,tmp} \ /srv/le-app-codex/nonproduction-secrets/generated \
/srv/le-payload-dev/artifacts/{browser,migration-reports} /srv/le-app-codex/container-runtime/docker \
/srv/le-app-codex/testing-data/{postgres,media,legacy-uploads,bgbye-models,tmp} \
/srv/le-app-codex/build-artifacts/{browser,migration-reports}
# Root owns the boundary and authoritative source; development owns mutable areas. # Root owns the boundary and authoritative source; development owns mutable areas.
sudo chown root:le_payload_dev /srv/le-payload-dev sudo chown root:le_app_codex /srv/le-app-codex
sudo chmod 0750 /srv/le-payload-dev sudo chmod 0750 /srv/le-app-codex
sudo chown -R le_payload_dev:le_payload_dev \ sudo chown le_app_codex:le_app_codex /srv/le-app-codex/home
/srv/le-payload-dev/lasereverything.net.db \ sudo chmod 0700 /srv/le-app-codex/home
/srv/le-payload-dev/runtime /srv/le-payload-dev/data /srv/le-payload-dev/artifacts \ sudo chown -R le_app_codex:le_app_codex \
/srv/le-payload-dev/secrets/generated /srv/le-app-codex/lasereverything.net.db \
sudo chmod 0700 /srv/le-payload-dev/runtime /srv/le-payload-dev/data \ /srv/le-app-codex/container-runtime /srv/le-app-codex/testing-data /srv/le-app-codex/build-artifacts \
/srv/le-payload-dev/artifacts /srv/le-payload-dev/secrets/generated /srv/le-app-codex/nonproduction-secrets/generated
sudo chown root:le_payload_dev /srv/le-payload-dev/source /srv/le-payload-dev/secrets sudo chmod 0700 /srv/le-app-codex/container-runtime /srv/le-app-codex/testing-data \
sudo chmod 0550 /srv/le-payload-dev/source /srv/le-app-codex/build-artifacts /srv/le-app-codex/nonproduction-secrets/generated
sudo chmod 0750 /srv/le-payload-dev/secrets sudo chown root:le_app_codex /srv/le-app-codex/database-source-readonly /srv/le-app-codex/nonproduction-secrets
sudo chmod 0550 /srv/le-app-codex/database-source-readonly
sudo chmod 0750 /srv/le-app-codex/nonproduction-secrets
# 5. Apply a 250 GiB ceiling to the project subvolume. # 6. Apply a 250 GiB ceiling to the project subvolume.
sudo btrfs quota enable /srv sudo btrfs quota enable /srv
sudo btrfs qgroup limit 250G /srv/le-payload-dev sudo btrfs qgroup limit 250G /srv/le-app-codex
# 6. Transitional source access is used only by the administrator-run copy and # 7. Transitional source access is used only by the administrator-run copy and
# verification procedure below. The final account needs no ACL on /srv/codex-*. # verification procedure below. The final account needs no ACL on /srv/codex-*.
# 7. Remediate production secret readability separately. # 8. Remediate production secret readability separately.
# Root-owned Compose can still read mode 0600 files. Confirm the production # Root-owned Compose can still read mode 0600 files. Confirm the production
# operator before applying because this changes production file permissions. # operator before applying because this changes production file permissions.
sudo chown root:root /var/www/lasereverything.net.db/app/.env.local sudo chown root:root /var/www/lasereverything.net.db/app/.env.local
@ -143,21 +186,21 @@ sudo chmod 0600 /var/www/lasereverything.net.db/app/.env.local
sudo chown root:root /var/www/lasereverything.net.db/bgbye/.env sudo chown root:root /var/www/lasereverything.net.db/bgbye/.env
sudo chmod 0600 /var/www/lasereverything.net.db/bgbye/.env sudo chmod 0600 /var/www/lasereverything.net.db/bgbye/.env
# 8. As le_payload_dev, configure the rootless daemon with an explicit data root. # 9. As le_app_codex, configure the rootless daemon with an explicit data root.
sudo -iu le_payload_dev mkdir -p ~/.config/docker sudo -iu le_app_codex mkdir -p ~/.config/docker
# Administrator writes ~/.config/docker/daemon.json as documented below, # Administrator writes ~/.config/docker/daemon.json as documented below,
# then the dedicated user enables the packaged rootless user service. # then the dedicated user enables the packaged rootless user service.
sudo -iu le_payload_dev systemctl --user enable --now docker.socket sudo -iu le_app_codex systemctl --user enable --now docker.socket
# 9. Optional only if reviewed stacks must survive logout. # 10. Optional only if reviewed stacks must survive logout.
sudo loginctl enable-linger le_payload_dev sudo loginctl enable-linger le_app_codex
``` ```
Proposed `/home/le_payload_dev/.config/docker/daemon.json`: Proposed `/srv/le-app-codex/home/.config/docker/daemon.json`:
```json ```json
{ {
"data-root": "/srv/le-payload-dev/runtime/docker", "data-root": "/srv/le-app-codex/container-runtime/docker",
"live-restore": false, "live-restore": false,
"log-driver": "local", "log-driver": "local",
"log-opts": { "max-size": "20m", "max-file": "5" } "log-opts": { "max-size": "20m", "max-file": "5" }
@ -166,9 +209,40 @@ Proposed `/home/le_payload_dev/.config/docker/daemon.json`:
The exact Arch rootless unit name (`docker.service` versus `docker.socket`) must be verified from the reviewed package before enabling. Cgroup v2 is present; after provisioning, verify the rootless/security options and delegated controllers before relying on CPU, memory, or PID limits. The exact Arch rootless unit name (`docker.service` versus `docker.socket`) must be verified from the reviewed package before enabling. Cgroup v2 is present; after provisioning, verify the rootless/security options and delegated controllers before relying on CPU, memory, or PID limits.
The nonstandard home is compatible with rootless Docker and user-level systemd because it is the account home recorded in `/etc/passwd`, resides on local Btrfs, and is owned by the account. Required environment contract:
- `HOME=/srv/le-app-codex/home` comes from the login/account record;
- `XDG_RUNTIME_DIR=/run/user/1200` is created by logind/systemd and must not be redirected into `/srv` or `/tmp`;
- `XDG_DATA_HOME=/srv/le-app-codex/home/.local/share`;
- Docker daemon data root is `/srv/le-app-codex/container-runtime/docker`;
- Docker client configuration is `/srv/le-app-codex/home/.docker`;
- user systemd configuration is `/srv/le-app-codex/home/.config/systemd/user`;
- the `le-app-codex` context points only to `unix:///run/user/1200/docker.sock`;
- wrappers export `COMPOSE_PROJECT_NAME=le-app-testing`.
Proposed `/srv/le-app-codex/home/.config/environment.d/10-le-app-codex.conf`:
```text
HOME=/srv/le-app-codex/home
XDG_DATA_HOME=/srv/le-app-codex/home/.local/share
DOCKER_CONFIG=/srv/le-app-codex/home/.docker
COMPOSE_PROJECT_NAME=le-app-testing
```
`XDG_RUNTIME_DIR` is deliberately absent from this file; logind must create `/run/user/1200` with the correct lifetime and ownership. `DOCKER_HOST` is also omitted so the named context remains the explicit authority and scripts can reject context drift.
After the user service is installed, create the context as the dedicated account:
```bash
sudo -iu le_app_codex docker context create le-app-codex \
--description 'Laser Everything Codex nonproduction rootless runtime' \
--docker host=unix:///run/user/1200/docker.sock
sudo -iu le_app_codex docker context use le-app-codex
```
## Required positive and negative verification ## Required positive and negative verification
Run as `le_payload_dev`, printing only permissions/status: Run as `le_app_codex`, printing only permissions/status:
```bash ```bash
test ! -r /var/www/lasereverything.net.db/app/.env.local test ! -r /var/www/lasereverything.net.db/app/.env.local
@ -176,10 +250,10 @@ test ! -x /var/lib/docker
test ! -x /var/lib/mysql test ! -x /var/lib/mysql
test ! -r /var/run/docker.sock test ! -r /var/run/docker.sock
test ! -x /srv/directus-archive test ! -x /srv/directus-archive
test -r /srv/le-payload-dev/source/README.txt test -r /srv/le-app-codex/database-source-readonly/README.txt
test ! -w /srv/le-payload-dev/source/README.txt test ! -w /srv/le-app-codex/database-source-readonly/README.txt
test -r /srv/le-payload-dev/secrets/migration.env test -r /srv/le-app-codex/nonproduction-secrets/migration.env
test ! -w /srv/le-payload-dev/secrets/migration.env test ! -w /srv/le-app-codex/nonproduction-secrets/migration.env
DOCKER_HOST=unix:///run/user/1200/docker.sock docker info --format '{{json .SecurityOptions}}' DOCKER_HOST=unix:///run/user/1200/docker.sock docker info --format '{{json .SecurityOptions}}'
``` ```
@ -192,12 +266,13 @@ Every stack/job wrapper will fail before Compose evaluation if:
- effective UID is not the approved dedicated UID; - effective UID is not the approved dedicated UID;
- Docker endpoint is not exactly the approved rootless Unix socket; - Docker endpoint is not exactly the approved rootless Unix socket;
- Compose project name lacks the fixed nonproduction prefix; - Compose project name lacks the fixed nonproduction prefix;
- active Docker context is not exactly `le-app-codex`;
- any resolved bind source is `/var/www`, `/var/lib/docker`, `/var/lib/mysql`, `/srv/directus-archive`, `/var/run`, `/run/docker.sock`, or a descendant; - any resolved bind source is `/var/www`, `/var/lib/docker`, `/var/lib/mysql`, `/srv/directus-archive`, `/var/run`, `/run/docker.sock`, or a descendant;
- the Git checkout is not exactly `/srv/le-payload-dev/lasereverything.net.db` in nonproduction or `/var/www/lasereverything.net.db` in production; - the Git checkout is not exactly `/srv/le-app-codex/lasereverything.net.db` in nonproduction or `/var/www/lasereverything.net.db` in production;
- any non-finite application service receives `.migration.env`, `.migration-source`, a MariaDB source URL, or migration credentials; - any non-finite application service receives `.migration.env`, `.migration-source`, a MariaDB source URL, or migration credentials;
- destination database host/port is not the approved nonproduction target/service; - destination database host/port is not the approved nonproduction target/service;
- production-named secret scopes, SMTP hosts, OAuth variables, Ko-fi secrets, or webhook credentials appear in fixture/snapshot/rehearsal rendered Compose; - production-named secret scopes, SMTP hosts, OAuth variables, Ko-fi secrets, or webhook credentials appear in fixture/snapshot/rehearsal rendered Compose;
- reset/purge targets a bind mount, external volume, unknown project label, or any path outside `/srv/le-payload-dev`. - reset/purge targets a bind mount, external volume, unknown project label, or any path outside `/srv/le-app-codex`.
Tests render each Compose mode and inspect mounts, secrets, environment variable **names/classifications**, networks, contexts, labels, and volume ownership without printing values. Tests render each Compose mode and inspect mounts, secrets, environment variable **names/classifications**, networks, contexts, labels, and volume ownership without printing values.
@ -206,12 +281,12 @@ Tests render each Compose mode and inspect mounts, secrets, environment variable
- The authoritative MariaDB remains read-only at the database privilege and filesystem layers. - The authoritative MariaDB remains read-only at the database privilege and filesystem layers.
- `.migration-source` and `.migration.env` are mounted read-only only into finite `inventory`, `import`, and `validation` jobs. - `.migration-source` and `.migration.env` are mounted read-only only into finite `inventory`, `import`, and `validation` jobs.
- Frontend, Payload, BGBye, proxy, Mailpit, fixture seed, package installation, build, lint, unit, integration, and ordinary browser-test services never receive those mounts or variables. - Frontend, Payload, BGBye, proxy, Mailpit, fixture seed, package installation, build, lint, unit, integration, and ordinary browser-test services never receive those mounts or variables.
- Legacy Directus restores the scrubbed SQL into a separate project-scoped writable MariaDB volume and copies uploads into `/srv/le-payload-dev/data/legacy-uploads` or a project volume. - Legacy Directus restores the scrubbed SQL into a separate project-scoped writable MariaDB volume and copies uploads into `/srv/le-app-codex/testing-data/legacy-uploads` or a project volume.
- Legacy Directus never mounts the source database storage or uploads, even read-only; the finite clone job is the only bridge. - Legacy Directus never mounts the source database storage or uploads, even read-only; the finite clone job is the only bridge.
- Reset/purge operates only on labeled nonproduction destinations. It does not follow source symlinks and refuses all external/bind volumes. - Reset/purge operates only on labeled nonproduction destinations. It does not follow source symlinks and refuses all external/bind volumes.
- `/srv/directus-archive` remains inaccessible and is never mentioned as a mount source in executable Compose configuration. - `/srv/directus-archive` remains inaccessible and is never mentioned as a mount source in executable Compose configuration.
The complete root-only archive remains permanently outside the development boundary at `/srv/directus-archive/20260710-175408`. It is never moved, mounted, linked, or copied into `/srv/le-payload-dev`. The complete root-only archive remains permanently outside the development boundary at `/srv/directus-archive/20260710-175408`. It is never moved, mounted, linked, or copied into `/srv/le-app-codex`.
## Nonproduction storage plan ## Nonproduction storage plan
@ -219,19 +294,19 @@ The host currently has approximately 650 GiB available on the Btrfs filesystem c
| Path | Planning allowance | Ownership | Cleanup / backup | | Path | Planning allowance | Ownership | Cleanup / backup |
|---|---:|---|---| |---|---:|---|---|
| `runtime/docker` | 80 GiB soft budget | `le_payload_dev` | prune only project/unused development images after digest recording; no backup | | `container-runtime/docker` | 80 GiB soft budget | `le_app_codex` | prune only project/unused development images after digest recording; no backup |
| `data/postgres` | 25 GiB | `le_payload_dev` / rootless mapped IDs | disposable; reset by labeled volume; no backup | | `testing-data/postgres` | 25 GiB | `le_app_codex` / rootless mapped IDs | disposable; reset by labeled volume; no backup |
| `data/media` | 15 GiB | fixed container UID mapped by rootless daemon | fixture disposable; snapshot copied data disposable; no authoritative backup role | | `testing-data/media` | 15 GiB | fixed container UID mapped by rootless daemon | fixture disposable; snapshot copied data disposable; no authoritative backup role |
| `data/legacy-uploads` | 10 GiB | rootless mapped service UID | copied from scrubbed source; disposable; never source of truth | | `testing-data/legacy-uploads` | 10 GiB | rootless mapped service UID | copied from scrubbed source; disposable; never source of truth |
| `data/bgbye-models` | 35 GiB | BGBye service/rootless mapping | checksummed cache; LRU/manual cleanup; redownloadable | | `testing-data/bgbye-models` | 35 GiB | BGBye service/rootless mapping | checksummed cache; LRU/manual cleanup; redownloadable |
| `data/tmp` | 10 GiB | service-specific mapped UID | tmpfs or age-based cleanup; no backup | | `testing-data/tmp` | 10 GiB | service-specific mapped UID | tmpfs or age-based cleanup; no backup |
| `artifacts/browser` | 5 GiB | `le_payload_dev` | TTL 7 days; snapshot artifacts treated as sensitive and never committed | | `build-artifacts/browser` | 5 GiB | `le_app_codex` | TTL 7 days; snapshot artifacts treated as sensitive and never committed |
| `artifacts/migration-reports` | 10 GiB | `le_payload_dev` | detailed reports TTL 30 days; committed summaries structural only | | `build-artifacts/migration-reports` | 10 GiB | `le_app_codex` | detailed reports TTL 30 days; committed summaries structural only |
| workspace/metadata headroom | 10 GiB | `le_payload_dev` | Git remote is source backup; no personal exports committed | | checkout/home metadata | 10 GiB | `le_app_codex` | Git remote is source backup; no personal exports committed |
The listed soft budgets total 200 GiB. The 250 GiB hard qgroup ceiling reserves 50 GiB emergency headroom. Startup refuses when host free space is below 100 GiB, project usage exceeds 200 GiB, or required free space for a rehearsal cannot be reserved. The production filesystem is not used for spillover. The listed soft budgets total 200 GiB. The 250 GiB hard qgroup ceiling reserves 50 GiB emergency headroom. Startup refuses when host free space is below 100 GiB, project usage exceeds 200 GiB, or required free space for a rehearsal cannot be reserved. The production filesystem is not used for spillover.
## Resources intentionally outside `/srv/le-payload-dev` ## Resources intentionally outside `/srv/le-app-codex`
| Resource | Permanent location | Why it remains outside | | Resource | Permanent location | Why it remains outside |
|---|---|---| |---|---|---|
@ -264,7 +339,7 @@ Metadata-only audit results at the planning checkpoint:
| Resource | Current state | | Resource | Current state |
|---|---| |---|---|
| `/srv/codex-work/lasereverything.net.db` | Primary Git checkout, branch `temporary/utilities-only`, commit `305ea41`; owns the current local Git database | | `/srv/codex-work/lasereverything.net.db` | Primary Git checkout, branch `temporary/utilities-only`, commit `305ea41`; owns the current local Git database |
| nested `.worktrees/payload-migration` | Migration worktree, branch `migration/payload`; planning commit begins at `ed1c84e` | | nested `.worktrees/payload-migration` | Migration worktree, branch `migration/le-app-database`; planning commit begins at `ed1c84e` |
| `.migration-source` | Symlink to `/srv/codex-migration/current` | | `.migration-source` | Symlink to `/srv/codex-migration/current` |
| `.migration.env` | Symlink to `/srv/codex-secrets/le-payload-migration.env` | | `.migration.env` | Symlink to `/srv/codex-secrets/le-payload-migration.env` |
| `/srv/codex-migration/current` | Symlink to `source-20260710-175408` | | `/srv/codex-migration/current` | Symlink to `source-20260710-175408` |
@ -297,7 +372,7 @@ No step deletes or mutates a transitional resource. Consolidation is copy/recrea
### Phase A — manifests and Git preservation ### Phase A — manifests and Git preservation
1. Push `migration/payload`, including planning commits, to `origin` and verify the remote commit. 1. Push `migration/le-app-database`, including planning commits, to `origin` and verify the remote commit.
2. Record structural manifests for every transitional path, symlink, owner/mode, aggregate size, Git branch/commit/remote, source checksum manifest, and the administrator container/volume inventory above. 2. Record structural manifests for every transitional path, symlink, owner/mode, aggregate size, Git branch/commit/remote, source checksum manifest, and the administrator container/volume inventory above.
3. Confirm the full root-only archive remains independently present and inaccessible; do not traverse or copy it. 3. Confirm the full root-only archive remains independently present and inaccessible; do not traverse or copy it.
4. Record the exact existing rootful container/volume names and leave them stopped/running exactly as found unless a later approved step says otherwise. 4. Record the exact existing rootful container/volume names and leave them stopped/running exactly as found unless a later approved step says otherwise.
@ -307,18 +382,18 @@ No step deletes or mutates a transitional resource. Consolidation is copy/recrea
After account/runtime approval, create one clean checkout as the dedicated user: After account/runtime approval, create one clean checkout as the dedicated user:
```bash ```bash
sudo -iu le_payload_dev git clone --branch migration/payload --single-branch \ sudo -iu le_app_codex git clone --branch migration/le-app-database --single-branch \
ssh://FORGE/makearmy/le-app.git \ ssh://FORGE/makearmy/le-app.git \
/srv/le-payload-dev/lasereverything.net.db /srv/le-app-codex/lasereverything.net.db
sudo -iu le_payload_dev git -C /srv/le-payload-dev/lasereverything.net.db \ sudo -iu le_app_codex git -C /srv/le-app-codex/lasereverything.net.db \
fetch --prune origin fetch --prune origin
sudo -iu le_payload_dev git -C /srv/le-payload-dev/lasereverything.net.db \ sudo -iu le_app_codex git -C /srv/le-app-codex/lasereverything.net.db \
status --short --branch status --short --branch
sudo -iu le_payload_dev git -C /srv/le-payload-dev/lasereverything.net.db \ sudo -iu le_app_codex git -C /srv/le-app-codex/lasereverything.net.db \
rev-parse HEAD rev-parse HEAD
``` ```
The HEAD must equal the reviewed remote `migration/payload` commit, the worktree must be clean, and no nested permanent worktree may exist. Provision a dedicated repository-scoped Git credential for `le_payload_dev`; do not copy `sol6_vi`'s private key. The HEAD must equal the reviewed remote `migration/le-app-database` commit, the worktree must be clean, and no nested permanent worktree may exist. Provision a dedicated repository-scoped Git credential for `le_app_codex`; do not copy `sol6_vi`'s private key.
### Phase C — scrubbed source copy ### Phase C — scrubbed source copy
@ -326,7 +401,7 @@ The administrator copies from the resolved source directory, never from the root
```bash ```bash
source_old=/srv/codex-migration/source-20260710-175408 source_old=/srv/codex-migration/source-20260710-175408
source_new=/srv/le-payload-dev/source source_new=/srv/le-app-codex/database-source-readonly
sudo rsync -aHAX --numeric-ids --delete-delay "$source_old/" "$source_new/" sudo rsync -aHAX --numeric-ids --delete-delay "$source_old/" "$source_new/"
@ -335,18 +410,18 @@ sudo sh -c "cd '$source_new' && sha256sum --check --status CHECKSUMS.sha256"
sudo sh -c "cd '$source_new' && sha256sum --check --status uploads.sha256" sudo sh -c "cd '$source_new' && sha256sum --check --status uploads.sha256"
# Freeze the canonical input against development writes. # Freeze the canonical input against development writes.
sudo chown -R root:le_payload_dev "$source_new" sudo chown -R root:le_app_codex "$source_new"
sudo find "$source_new" -type d -exec chmod 0550 {} + sudo find "$source_new" -type d -exec chmod 0550 {} +
sudo find "$source_new" -type f -exec chmod 0440 {} + sudo find "$source_new" -type f -exec chmod 0440 {} +
``` ```
Before using `--delete-delay`, assert `source_new` resolves exactly beneath `/srv/le-payload-dev/source` and is not a symlink. The source checksum files, row-count manifests, schema, dump, extensions, and all uploads must validate. The old copy remains untouched. Before using `--delete-delay`, assert `source_new` resolves exactly to `/srv/le-app-codex/database-source-readonly` and is not a symlink. The source checksum files, row-count manifests, schema, dump, extensions, and all uploads must validate. The old copy remains untouched.
### Phase D — nonproduction secrets ### Phase D — nonproduction secrets
- Preserve both old credential files in place during rollback. - Preserve both old credential files in place during rollback.
- Do not copy `le-payload-migration-admin.env`. - Do not copy `le-payload-migration-admin.env`.
- Generate new fixture/snapshot/rootless database, Payload, Mailpit, and local gateway credentials directly into root-owned `/srv/le-payload-dev/secrets` with mode `0640`. - Generate new fixture/snapshot/rootless database, Payload, Mailpit, and local gateway credentials directly into root-owned `/srv/le-app-codex/nonproduction-secrets` with mode `0640`.
- If the MariaDB read-only credential from the scrubbed environment is temporarily required to compare the old rootful source, transfer only the minimum required variable set through an administrator-reviewed, non-logging process; replace it once the rootless source clone is verified. - If the MariaDB read-only credential from the scrubbed environment is temporarily required to compare the old rootful source, transfer only the minimum required variable set through an administrator-reviewed, non-logging process; replace it once the rootless source clone is verified.
- No production SMTP, OAuth, Ko-fi, Directus token, webhook, or server credential enters the new directory. - No production SMTP, OAuth, Ko-fi, Directus token, webhook, or server credential enters the new directory.
@ -387,7 +462,7 @@ Every deletion requires a separate explicit approval showing the resolved path/o
Promotion path: Promotion path:
```text ```text
/srv/le-payload-dev/lasereverything.net.db /srv/le-app-codex/lasereverything.net.db
-> reviewed Git commit + signed/recorded release manifest -> reviewed Git commit + signed/recorded release manifest
-> CI-built immutable image digests tested in fixture/snapshot/rehearsal/staging -> CI-built immutable image digests tested in fixture/snapshot/rehearsal/staging
-> clean checkout or verified release artifact at /var/www/lasereverything.net.db -> clean checkout or verified release artifact at /var/www/lasereverything.net.db
@ -405,7 +480,7 @@ Required workflow:
The production checkout contains all application source/configuration needed to reproduce the deployment contract. Physical image layers, declared production PostgreSQL/media volumes, and shared host-proxy configuration may remain outside it, but their names/locations/interfaces are declared in the production overlay and release manifest rooted there. The production checkout contains all application source/configuration needed to reproduce the deployment contract. Physical image layers, declared production PostgreSQL/media volumes, and shared host-proxy configuration may remain outside it, but their names/locations/interfaces are declared in the production overlay and release manifest rooted there.
Forbidden promotion inputs include `/srv/le-payload-dev/runtime`, `source`, `secrets`, `data`, `artifacts`, local CA material, Mailpit state, test traces, rootless Docker state, disposable databases/volumes, caches, and development `.env` files. No `rsync` or recursive copy from `/srv/le-payload-dev` to `/var/www` is permitted. Forbidden promotion inputs include `/srv/le-app-codex/container-runtime`, `database-source-readonly`, `nonproduction-secrets`, `testing-data`, `build-artifacts`, local CA material, Mailpit state, test traces, rootless Docker state, disposable databases/volumes, caches, and development `.env` files. No `rsync` or recursive copy from `/srv/le-app-codex` to `/var/www` is permitted.
## Local HTTPS and CA trust ## Local HTTPS and CA trust
@ -416,9 +491,9 @@ The local gateway uses a repository-pinned proxy image and an ignored, generated
- `mail.le.localhost`; - `mail.le.localhost`;
- optional `legacy.le.localhost`. - optional `legacy.le.localhost`.
All bind to loopback port 8443, avoiding privileged ports and host DNS changes (`*.localhost` resolves locally). Certificates and private keys live under `/srv/le-payload-dev/secrets/local-ca`, mode `0700/0600`, excluded from Git and build contexts. All bind to loopback port 8443, avoiding privileged ports and host DNS changes (`*.localhost` resolves locally). Certificates and private keys live under `/srv/le-app-codex/nonproduction-secrets/local-ca`, mode `0700/0600`, excluded from Git and build contexts.
Automated Playwright tests may use a dedicated browser profile with its own trust database. Manual Chromium trust can be limited to `/home/le_payload_dev/.pki/nssdb` using the already installed `certutil`; system-wide CA trust is unnecessary. Importing the CA into even that user/browser profile is a host/user-state change and requires explicit approval. Removal deletes the certificate from that profile and the ignored CA directory. Automated Playwright tests may use a dedicated browser profile with its own trust database. Manual Chromium trust can be limited to `/srv/le-app-codex/home/.pki/nssdb` using the already installed `certutil`; system-wide CA trust is unnecessary. Importing the CA into even that user/browser profile is a host/user-state change and requires explicit approval. Removal deletes the certificate from that profile and the ignored CA directory.
## Mailpit and outbound protection ## Mailpit and outbound protection
@ -465,28 +540,28 @@ CPU and GPU images share server contract tests. Capability differences appear in
Rollback must be run only after confirming the selected context/socket is the dedicated one: Rollback must be run only after confirming the selected context/socket is the dedicated one:
```bash ```bash
# As le_payload_dev: stop project resources, then the user daemon. # As le_app_codex: stop project resources, then the user daemon.
DOCKER_HOST=unix:///run/user/1200/docker.sock docker compose \ DOCKER_HOST=unix:///run/user/1200/docker.sock docker compose \
--project-name le_payload_dev down --remove-orphans --project-name le-app-testing down --remove-orphans
systemctl --user disable --now docker.socket docker.service systemctl --user disable --now docker.socket docker.service
# As administrator: disable optional lingering. # As administrator: disable optional lingering.
sudo loginctl disable-linger le_payload_dev sudo loginctl disable-linger le_app_codex
# Verify production daemon endpoint and data were never referenced by project metadata/logs. # Verify production daemon endpoint and data were never referenced by project metadata/logs.
# Use the planning preflight/audit script; do not connect to or mutate production Docker. # Use the planning preflight/audit script; do not connect to or mutate production Docker.
# Remove only the dedicated project subvolume after an explicit artifact review. # Remove only the dedicated project subvolume after an explicit artifact review.
sudo btrfs subvolume delete /srv/le-payload-dev sudo btrfs subvolume delete /srv/le-app-codex
# Remove subordinate allocations and account after the subvolume/runtime is gone. # Remove subordinate allocations and account after the subvolume/runtime is gone.
sudo usermod --del-subuids 165536-231071 --del-subgids 165536-231071 le_payload_dev sudo usermod --del-subuids 165536-231071 --del-subgids 165536-231071 le_app_codex
sudo userdel --remove le_payload_dev sudo userdel --remove le_app_codex
sudo groupdel le_payload_dev 2>/dev/null || true sudo groupdel le_app_codex 2>/dev/null || true
``` ```
The dedicated Docker context lives only in the dedicated user's home and disappears with that home. If another operator created a client context pointing to the user socket, remove that context explicitly from that operator's client configuration. The dedicated Docker context lives only in the dedicated user's home and disappears with that home. If another operator created a client context pointing to the user socket, remove that context explicitly from that operator's client configuration.
Production secret permission hardening is not rolled back merely because development is removed; it is an independent security correction. Any temporary transitional ACLs, if an administrator added them contrary to the final copy-based design, must be inventoried and removed before account deletion. Removal does not touch `/var/run/docker.sock`, production contexts, `/var/lib/docker`, `/var/lib/mysql`, `/var/www` application content, production volumes/networks, transitional source during its rollback retention, or `/srv/directus-archive`. Production secret permission hardening is not rolled back merely because development is removed; it is an independent security correction. Any temporary transitional ACLs, if an administrator added them contrary to the final copy-based design, must be inventoried and removed before account deletion. Removal does not touch `/var/run/docker.sock`, production contexts, `/var/lib/docker`, `/var/lib/mysql`, `/var/www` application content, production volumes/networks, transitional source during its rollback retention, or `/srv/directus-archive`.
Final proof compares before/after permission metadata and confirms: production socket remains denied, production service state is unchanged, no project mount referenced forbidden paths, the dedicated UID/subordinate mappings are gone, and only `/srv/le-payload-dev` resources were deleted. Final proof compares before/after permission metadata and confirms: production socket remains denied, production service state is unchanged, no project mount referenced forbidden paths, the dedicated UID/subordinate mappings are gone, and only `/srv/le-app-codex` resources were deleted.

View file

@ -1,4 +1,4 @@
# Payload transition implementation plan # Laser Everything database transition implementation plan
Status: revised pre-implementation plan. Work stops at the Milestone 0 host-runtime approval gate. Status: revised pre-implementation plan. Work stops at the Milestone 0 host-runtime approval gate.
@ -9,7 +9,7 @@ Status: revised pre-implementation plan. Work stops at the Milestone 0 host-runt
- Review `architecture-proposal.md`, `environment-parity.md`, and host prerequisites. - Review `architecture-proposal.md`, `environment-parity.md`, and host prerequisites.
2. **Host runtime gate — requires human approval/provisioning** 2. **Host runtime gate — requires human approval/provisioning**
- Provision the dedicated `le_payload_dev` account and a rootless Docker context owned only by it. - Provision the dedicated `le_app_codex` account and a rootless Docker context owned only by it.
- Restrict production environment files currently readable to `sol6_vi`; verify the development account cannot traverse production paths. - Restrict production environment files currently readable to `sol6_vi`; verify the development account cannot traverse production paths.
- Use deterministic CPU BGBye initially; defer optional rootless GPU provisioning. - Use deterministic CPU BGBye initially; defer optional rootless GPU provisioning.
- Approve browser-profile-scoped local CA trust for HTTPS testing. - Approve browser-profile-scoped local CA trust for HTTPS testing.
@ -19,8 +19,8 @@ Status: revised pre-implementation plan. Work stops at the Milestone 0 host-runt
3. **Reversible canonical-path consolidation — separately approval-gated** 3. **Reversible canonical-path consolidation — separately approval-gated**
- Inventory the transitional Git roots, symlinks, credential filenames, rootful disposable containers, and volumes without reading secret values. - Inventory the transitional Git roots, symlinks, credential filenames, rootful disposable containers, and volumes without reading secret values.
- Create the sole checkout at `/srv/le-payload-dev/lasereverything.net.db` and verify `migration/payload` commit/remote state. - Create the sole checkout at `/srv/le-app-codex/lasereverything.net.db` and verify `migration/le-app-database` commit/remote state.
- Copy and checksum the scrubbed source into root-owned read-only `/srv/le-payload-dev/source`. - Copy and checksum the scrubbed source into root-owned read-only `/srv/le-app-codex/database-source-readonly`.
- Create new nonproduction-only secrets and rootless MariaDB/PostgreSQL resources; do not reuse production credentials. - Create new nonproduction-only secrets and rootless MariaDB/PostgreSQL resources; do not reuse production credentials.
- Preserve all old resources through the rollback period. Delete nothing without a later explicit approval. - Preserve all old resources through the rollback period. Delete nothing without a later explicit approval.