Files
sunfish-admin 12d2179c20 AXP Magisk resurrection track: Speckit 009 port AXP/LOS22 to LOS23.
Ship Magisk like GOLDEN; KernelSU optional recycle only; parallel to sunfish-a16-rom standby.
2026-09-16 21:56:22 +00:00

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=base provisioning 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.0 without 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:

  1. .specify/memory/constitution.md v0.1.5
  2. specs/001-development-environment/spec.md
  3. AGENTS.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-dev01 exists (human + SSYSTEMS Salt/PVE)
  • Ubuntu Server 24.04 LTS
  • One reachable public IPv4 (public NIC only)
  • SSH administration works
  • saltenv=base already 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 sync or 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 777 project 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.0 without 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-build owns /srv/rom trees 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 is 002) or generate secrets.
  • artifacts/ must be writable by android-build for 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 -j from vCPU/RAM here.
  • FR-007: document capacity (6/52/32) and that conservative -j / OOM tuning belongs to 003, with a one-line recommended starting point only if needed for docs — no build execution in 001.
  • 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-build on the host if needed. No repo 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-dev01 with persistent data dirs and systemd unit.
  • Default UI access: loopback / non-public bind + SSH tunnel from laptop (decision fixed for 001 unless 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:

  1. sunfish-a16-rom — Spec Kit + specs + docs; push existing history
  2. sunfish-rom-manifest — empty
  3. sunfish-rom-build — scripts + Ansible + Semaphore/build automation (seed minimal structure when ready)
  4. 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-dev01 for project environment automation.
  • Keep playbooks and automation in sunfish-rom-build (not in the Spec Kit meta repo sunfish-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:

  1. Direct SSH from laptop as android-build (pubkey); Cursor Remote SSH → /home/android-build/sunfish-a16-rom with User android-build
  2. Dummy artifact in /srv/rom/artifacts → rsync to laptop → SHA256 match
  3. 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

  1. Host preflight baseline
  2. Create android-build
  3. Configure developer SSH public-key access for android-build
  4. Verify direct SSH login from the laptop as android-build
  5. Create /srv/rom + permissions
  6. Install build prerequisites (current LOS/AOSP docs)
  7. repo / git / git-lfs / ccache config
  8. Copy laptop staging meta repo to host path /home/android-build/sunfish-a16-rom (preserve .git)
  9. Clean-shell toolchain smoke via SSH as android-build
  10. Cursor Remote SSH smoke (User android-build)
  11. Dummy artifact → rsync → laptop (SHA256)

P2 — reproducible / comfortable

  1. Install Gitea (loopback/tunnel)
  2. Create four repos
  3. Push meta repo; set Gitea canonical
  4. Off-VM bare mirrors on laptop
  5. Install Ansible
  6. First smoke playbook
  7. Install Semaphore
  8. Run smoke playbook from Semaphore
  9. Isolation / access / firewall validation (SSH-safe)
  10. 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.