Ship Magisk like GOLDEN; KernelSU optional recycle only; parallel to sunfish-a16-rom standby.
19 KiB
Implementation Plan: Development environment on rom-dev01
Branch: 001-development-environment | Date: 2026-08-09 | Spec: spec.md
Input: Feature specification from /specs/001-development-environment/spec.md
Mode: Documentation/design only until rom-dev01 exists with working SSH.
No implementation, host mutation, or Android sync/build in this planning phase.
Summary
Feature 001 prepares an already-existing Ubuntu 24.04 host (rom-dev01) as the
ROM development environment: unprivileged android-build account, /srv/rom
layout, Lineage/AOSP host toolchain + ccache, Spec Kit meta-repo bootstrap,
private Gitea (four repos) with off-VM Git history mirror, Ansible + Semaphore
smoke orchestration, thin-client laptop workflow, and guest isolation posture.
It does not create the Proxmox VM, sync LineageOS, build Android, or touch
the Pixel. Those belong to human Salt/PVE prep (rom-dev01 prerequisite) and
features 002/003+.
Unlocks: after P1, host is usable for work; after P2, Gitea is canonical and
orchestration smoke works → ready for 002 then 003.
Technical Context
Language/Version: Bash / Ansible YAML; Ubuntu Server 24.04 LTS on host; Spec Kit docs in Markdown (English).
Primary Dependencies: apt packages per current LineageOS/AOSP host build
docs at implementation time; repo; git + git-lfs; ccache; Gitea;
Ansible; Semaphore UI; OpenSSH.
Storage: Host filesystem paths under /srv/rom/ (no project-owned LVM/fstab
changes). Secrets directory restricted/empty in 001.
Testing: Observable shell/service checks (clean-login tool smoke, systemd active, local curl, rsync SHA256, restart survival). No Android instrumentation.
Target Platform: Single VM rom-dev01 (~6 vCPU / 52 GB RAM / 2 TB /
32 GB swap) + thin laptop (Cursor Remote SSH, adb/fastboot, rsync).
Project Type: Infrastructure / host bootstrap for Android ROM development.
Performance Goals: Toolchain available in a clean android-build login;
services survive restart; no public exposure of Gitea/Semaphore. Build -j
tuning is out of scope (owned by 003).
Constraints:
- VM/PVE/IP/Salt
saltenv=baseprovisioning is external; do not fight managed config silently. - Never build Android as root; never
chmod 777. - Never flush firewall / risk active SSH path.
- Do not bind Gitea/Semaphore to
0.0.0.0without an explicit protected design (default: loopback / non-public + SSH tunnel). - No secrets in Git or build logs.
- Do not delete laptop staging tree before Gitea + mirror verified.
Scale/Scope: One developer; four Gitea repos; one smoke Ansible playbook; no distributed CI / registry / Kubernetes.
Constitution Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
| Gate | Status | Notes |
|---|---|---|
| Independence from SSYSTEMS governance | PASS | No Redmine/GitLab/company MR workflow; no Salt VM creation from this repo |
| Spec-as-source | PASS | Plan derived from 001/spec.md + constitution 0.1.5 |
| Parity/hardening docs non-normative | PASS | Not used as requirements for 001 |
| Host boundary: VM is prerequisite | PASS | Preconditions assume host exists; non-goals forbid PVE/qm/Salt states |
| Isolation posture (guest) | PASS | Public-NIC workflow; UIs not world-exposed; SSH preserved |
| No Android trees on laptop | PASS | Thin-client + /srv/rom on host |
| Secrets not in Git | PASS | Explicit secrets handling |
Magisk/KSU/microG/signing out of 001 |
PASS | Listed in DO NOT |
| Capacity honesty | PASS | Plan around 6/52/2T/32; parallelism deferred to 003 |
Post-design re-check: PASS — artifacts (research.md, quickstart.md,
operational data-model.md) stay inside 001 scope; no invented API contracts.
Project Structure
Documentation (this feature)
specs/001-development-environment/
├── plan.md # This file
├── research.md # Phase 0
├── data-model.md # Operational entities only (no app domain model)
├── quickstart.md # Validation / cold-start guide
├── contracts/ # N/A for 001 (see contracts/README.md)
└── tasks.md # Created later by /speckit-tasks — NOT this command
Host layout (target on rom-dev01)
/srv/rom/
├── src/ # future Android trees (empty in 001; no repo sync)
├── references/ # future AXP/reference captures (empty OK)
├── tooling/ # project scripts / ansible / helper tooling
├── ccache/ # CCACHE_DIR for android-build
├── artifacts/ # handoff to laptop (dummy file for smoke)
├── logs/ # install/bootstrap logs
└── secrets/ # restricted; empty; no key generation in 001
# service data (exact paths chosen at implement time; document them)
# e.g. /var/lib/gitea, /etc/gitea, semaphore data dir
# Canonical meta Spec Kit working tree (Cursor Remote SSH edits here):
/home/android-build/sunfish-a16-rom
# /srv/rom/tooling/ = project tooling only (scripts helpers, etc.)
# — NOT the Spec Kit meta checkout
Repository roles (Gitea)
| Repo | Contents |
|---|---|
sunfish-a16-rom |
Spec Kit + specs + docs (meta) |
sunfish-rom-build |
Scripts + Ansible + Semaphore/build automation |
sunfish-rom-manifest |
Repo manifests (empty at first) |
sunfish-rom-patches |
Patchsets / tracking (empty at first) |
Do not mix executable automation into the Spec Kit meta repo.
Meta repository content (laptop staging → Gitea)
/home/android-build/sunfish-a16-rom/ # Spec Kit + docs only
├── .specify/
├── specs/
├── docs/
├── AGENTS.md
└── …
Structure Decision: Infrastructure feature — host paths + Git/service
bootstrap. No application src/ tree inside Spec Kit for 001.
Complexity Tracking
No constitution violations requiring justification.
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| — | — | — |
1. Summary (expanded)
001 makes rom-dev01 a usable ROM workstation and private Git home without
performing ROM builds. Completion means: clean android-build toolchain smoke
passes; /srv/rom owned correctly; Spec Kit tree lives on host; Gitea holds
four repos and is canonical; laptop has an off-VM bare mirror; Ansible+Semaphore
run one smoke playbook; thin-client + isolation docs/checks pass; no PVE/Salt
fight, no Pixel/LOS work.
2. Constitution check / source of truth
Authoritative for this plan:
.specify/memory/constitution.mdv0.1.5specs/001-development-environment/spec.mdAGENTS.md
Non-normative (do not treat as 001 requirements):
docs/axp-pro-parity.md, docs/hardening-backlog.md.
3. Preconditions
Assume already true before /speckit-implement:
rom-dev01exists (human + SSYSTEMS Salt/PVE)- Ubuntu Server 24.04 LTS
- One reachable public IPv4 (public NIC only)
- SSH administration works
saltenv=basealready applied- Capacity approximately: 6 vCPU / 52 GB RAM / 2 TB disk / 32 GB swap
Do not create, resize, or re-IP the VM from this project.
4. Non-goals / boundaries
DO NOT (plan / tasks / implement):
- create or configure the Proxmox VM; emit
qm/PVE provisioning; - choose or hard-code the public IPv4;
- write SSYSTEMS Salt states; modify
pm-test01; - assume company internal DNS/VPN/LDAP/GitLab/NFS/Zabbix is required;
- start LineageOS
repo syncor any Android build; - touch/backup/flash the Pixel (NeoBackup belongs to
002); - introduce microG, Phonesky, KernelSU, Magisk, SUSFS, or hardening;
- generate ROM/AVB/Phonesky signing keys;
- put
.repo,out/, or ccache on the laptop; - require Ansible on the laptop;
- expose Gitea or Semaphore to the public Internet;
- design GitLab/Jenkins/Kubernetes/registry/distributed runners;
- turn Semaphore into the Android build pipeline yet;
- invent an API/data model merely to satisfy a template;
- hard-code facts that must be measured on
rom-dev01; - duplicate requirements owned by
002/003+.
Implement-time DO NOT (operational):
- run Android builds as root;
chmod 777project trees; - blindly dist-upgrade; replace apt sources without need;
- overwrite Salt/base-managed config silently;
- flush firewall/nftables; risk active SSH management path;
- bind Gitea/Semaphore to
0.0.0.0without explicit protected design; - commit passwords/tokens/private keys; plaintext secrets in Ansible in Git;
- delete laptop staging before Gitea + mirror verified;
- delete/overwrite working data only to fake idempotency;
- download full Lineage tree; compile Lineage; connect/flash Pixel;
- automate ROM builds through Semaphore yet.
Conflict rule: If base-managed configuration conflicts with project needs, stop and report — do not silently replace or fight it; do not “fix” unrelated infrastructure opportunistically.
5. Host preflight (before modifying the host)
First implement steps are observational. Capture and store under
/srv/rom/logs/preflight/ (or create layout first then log):
- Ubuntu version / kernel
- CPU / RAM / swap
- filesystem free space (esp. for
/and future/srv/rom) - network interfaces / default route (confirm no required internal NIC)
- SSH reachability
- existing users / groups
- existing firewall (nft/ufw/iptables) summary — do not flush
- listening ports / services
- apt sources summary
- hints of Salt/base-managed files if obvious
Produce a short baseline note in the meta repo docs or /srv/rom/logs for later
diff. Especially important because saltenv=base already touched the machine.
6. Account and permission model
| Actor | Role |
|---|---|
| Existing admin SSH user | packages, systemd, system config, firewall changes (carefully) |
android-build |
Android sources/builds (later), ccache, artifacts, build tooling; SSH pubkey login from laptop; Cursor Remote SSH user; never build as root; no sudo required to compile |
gitea (service user) |
Gitea data/config ownership |
semaphore (service user/context) |
Semaphore data/config ownership |
Rules:
- No
chmod -R 777 /srv/rom - Group memberships only as needed (e.g.
android-buildowns/srv/romtrees it writes; services own their state dirs) - Admin elevates only for system packaging/services
7. /srv/rom filesystem layout
Create with explicit owners/modes (exact modes chosen at implement; document):
/srv/rom/
├── src/
├── references/
├── tooling/
├── ccache/
├── artifacts/
├── logs/
└── secrets/ # empty, restricted; do not generate keys in 001
- Do not manage partitions/LVM/fstab from this project.
- Do not populate
references/(that is002) or generate secrets. artifacts/must be writable byandroid-buildfor dummy + later builds.
8. Android host toolchain (no sync/build)
- Install host packages using current primary LineageOS/AOSP build documentation at implementation time (do not freeze a stale 50-package list in this plan).
- Record what was installed (log + short note in meta repo /
/srv/rom/logs). - Ensure
git,git-lfs,repo,python3, and Java/toolchain prerequisites required by that documentation. - Configure
ccache: location (/srv/rom/ccache), ownership (android-build), size cap, environment (CCACHE_DIR, etc.). Not tied to inventing-jfrom vCPU/RAM here. - FR-007: document capacity (6/52/32) and that conservative
-j/ OOM tuning belongs to003, with a one-line recommended starting point only if needed for docs — no build execution in001. - Smoke from a clean login: primary check is direct SSH from the laptop as
android-build(after pubkey setup) → verify tools. Optional local admin check:sudo -iu android-buildon the host if needed. Norepo sync.
9. Spec Kit staging bootstrap (before Gitea)
Egg/chicken sequence (mandatory):
laptop staging:
/home/aventura/Workspaces/sunfish-a16-rom
↓ rsync/scp/git bundle (preserve .git)
rom-dev01 working copy
↓
install Gitea
↓
create empty Gitea repo sunfish-a16-rom
↓
push meta repo
↓
create sunfish-rom-manifest, sunfish-rom-build, sunfish-rom-patches
↓
Gitea becomes canonical remote
↓
host working tree at /home/android-build/sunfish-a16-rom
uses Gitea as origin (after push)
Do not assume git clone …gitea… before Gitea exists.
Do not delete laptop staging until Gitea push + off-VM mirror verified.
10. Gitea
- Install on
rom-dev01with persistent data dirs and systemd unit. - Default UI access: loopback / non-public bind + SSH tunnel from laptop
(decision fixed for
001unless implement discovers a hard conflict — then stop/report). - Git transport: document the concrete mechanism at implement time after
inspecting the host (HTTP via the same SSH tunnel and/or Git-over-SSH also
tunnelled are valid). Do not blindly modify OpenSSH managed by
saltenv=base, and do not assume or open a second public SSH port (e.g.:2222) for Gitea. - Create admin credentials with restricted local storage; never commit secrets.
- Verify: service active; local curl succeeds; direct public Internet access to UI not required / not open.
11. Canonical Git transition
Repos:
sunfish-a16-rom— Spec Kit + specs + docs; push existing historysunfish-rom-manifest— emptysunfish-rom-build— scripts + Ansible + Semaphore/build automation (seed minimal structure when ready)sunfish-rom-patches— empty
Document remotes for host + laptop clones. Company GitLab must not be used.
12. Off-VM Git mirror (P2)
First implementation (sufficient for 001 SC-007): laptop-initiated pull
of bare mirrors. The laptop opens the connection (via SSH tunnel / documented
Git URL); rom-dev01 must not need inbound SSH to the laptop.
# On laptop (conceptual):
git clone --mirror <gitea-repo-url-via-tunnel>
cd <repo>.git && git remote update
Borg / full Gitea DR is later. Requirement is Git history redundancy.
13. Ansible bootstrap (P2)
- Install Ansible on
rom-dev01for project environment automation. - Keep playbooks and automation in
sunfish-rom-build(not in the Spec Kit meta reposunfish-a16-rom). - No plaintext secrets in Git.
14. Semaphore bootstrap (P2)
- Install Semaphore as UI over Ansible.
- Same exposure model as Gitea (loopback/tunnel).
- First integration: unprivileged environment smoke playbook only — e.g.
gather facts + assert
/srv/rom,android-build, and toolchain checks. No sudo password / privileged secrets in Semaphore for this smoke. Privileged automation is a later explicit decision. - Not an Android build pipeline.
15. Isolation / firewall / service exposure
Keep guest isolation posture from constitution/001 spec:
- Public NIC only for project workflow
- Gitea/Semaphore not world-exposed
- Document firewall/DNS expectations
Implementation safety:
- never flush/replace existing firewall blindly;
- never close current SSH path before a tested replacement;
- keep a rollback path;
- prefer additive allow rules.
16. Laptop thin-client workflow
Document and smoke:
- Direct SSH from laptop as
android-build(pubkey); Cursor Remote SSH →/home/android-build/sunfish-a16-romwithUser android-build - Dummy artifact in
/srv/rom/artifacts→rsyncto laptop → SHA256 match - adb/fastboot remain on laptop; Pixel USB on laptop; no USB passthrough design
17. Verification matrix (FR/SC → evidence)
| ID | Implementation focus | Observable verification |
|---|---|---|
| FR-001 | Create android-build + home/workspace docs + SSH pubkey |
id android-build; ssh android-build@rom-dev01 from laptop |
| FR-002 | LOS/AOSP host deps + ccache | clean SSH login as android-build: git, repo, ccache -s, etc. |
| FR-003 | /srv/rom tree + ownership |
namei -l / stat shows owners; writable artifacts |
| FR-004 | Gitea + four repos | local UI/API; four repos listed |
| FR-005 | Ansible + Semaphore smoke | Semaphore runs smoke playbook successfully |
| FR-006 | Thin-client docs | doc exists; Remote SSH + rsync smoke done |
| FR-007 | Capacity / defer -j to 003 |
short doc records 6/52/32 and defers build jobs |
| FR-008 | Spec Kit on host | tree present; after P2, cloned/pushed via Gitea |
| FR-009 | Isolation posture | checklist: NIC, binds, firewall notes; SSH still works |
| FR-010 | Off-VM Git mirror | laptop bare mirror of four repos once |
| SC-001 | Toolchain smoke | clean android-build session has tools for later 003 |
| SC-002 | Artifacts handoff | dummy file rsync SHA256 match |
| SC-003 | Four Gitea repos | listed on Gitea; not company GitLab |
| SC-004 | Cold-start workflow doc | quickstart path SSH→workspace→(later build)→rsync→flash |
| SC-005 | No PVE management | no qm/PVE playbooks in repo/tasks evidence |
| SC-006 | Isolation checklist | passes without requiring VPN/internal DNS |
| SC-007 | Mirror demonstrated | off-VM mirror exists and was verified once |
18. Handoff
001 complete means environment ready for 002 (phone capture) and later
003 (LOS vanilla build) — not “start building Lineage now”.
Do not begin /speckit-plan for 002/003 until 001 doc cycle + implement
are done (avoid document saturation).
P1 / P2 sequencing
P1 — host usable without Gitea
- Host preflight baseline
- Create
android-build - Configure developer SSH public-key access for
android-build - Verify direct SSH login from the laptop as
android-build - Create
/srv/rom+ permissions - Install build prerequisites (current LOS/AOSP docs)
repo/git/git-lfs/ ccache config- Copy laptop staging meta repo to host path
/home/android-build/sunfish-a16-rom(preserve.git) - Clean-shell toolchain smoke via SSH as
android-build - Cursor Remote SSH smoke (
User android-build) - Dummy artifact → rsync → laptop (SHA256)
P2 — reproducible / comfortable
- Install Gitea (loopback/tunnel)
- Create four repos
- Push meta repo; set Gitea canonical
- Off-VM bare mirrors on laptop
- Install Ansible
- First smoke playbook
- Install Semaphore
- Run smoke playbook from Semaphore
- Isolation / access / firewall validation (SSH-safe)
- Service restart (+ reboot survival where practical)
Finish P2 before formally starting 002.
Phase outputs
| Artifact | Role |
|---|---|
| research.md | Decisions: Gitea/Semaphore access, mirror, deps source, ccache |
| data-model.md | Operational entities only |
| quickstart.md | Cold-start validation |
| contracts/ | N/A |
Next command after human review of these files: /speckit-tasks (still no VM).
Then /speckit-analyze. Stop before /speckit-implement until SSH to
rom-dev01 works.