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.
This commit is contained in:
commit
12d2179c20
150 files changed
+26263
No files matched your search
@@ -0,0 +1,9 @@
|
||||
# Contracts: N/A for 001
|
||||
|
||||
Feature `001-development-environment` is host/Git/service bootstrap.
|
||||
|
||||
It does **not** expose a project-owned HTTP API, CLI contract for external
|
||||
consumers, or IPC schema that needs `contracts/` files.
|
||||
|
||||
Service access is operational (SSH tunnel to Gitea/Semaphore UIs) and is
|
||||
documented in `plan.md` / `quickstart.md` instead.
|
||||
@@ -0,0 +1,87 @@
|
||||
# Data model (operational): 001-development-environment
|
||||
|
||||
This is **not** an application domain model. It records host/Git/service
|
||||
entities for planning clarity. No ORM, no API schemas.
|
||||
|
||||
## Entities
|
||||
|
||||
### Host (`rom-dev01`)
|
||||
|
||||
| Field | Notes |
|
||||
|-------|-------|
|
||||
| OS | Ubuntu 24.04 LTS (precondition) |
|
||||
| Capacity | ~6 vCPU / 52 GB / 2 TB / 32 GB swap |
|
||||
| Network | Public IPv4 NIC; no required internal NIC |
|
||||
| Management | External Salt `saltenv=base` + project config |
|
||||
|
||||
### Account `android-build`
|
||||
|
||||
| Field | Notes |
|
||||
|-------|-------|
|
||||
| Type | Unprivileged login user |
|
||||
| Owns | `/srv/rom/{src,references,tooling,ccache,artifacts,logs}` (as documented) |
|
||||
| Must not | Need root to compile; own Gitea/Semaphore system dirs |
|
||||
|
||||
### Meta working tree
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `/home/android-build/sunfish-a16-rom` | Spec Kit + specs + docs (Cursor Remote SSH) |
|
||||
|
||||
### Path root `/srv/rom`
|
||||
|
||||
| Child | Purpose | Populated in 001? |
|
||||
|-------|---------|-------------------|
|
||||
| `src/` | Future Android trees | Empty (no sync) |
|
||||
| `references/` | Future AXP/reference | Empty |
|
||||
| `tooling/` | Project tooling helpers (not Spec Kit checkout) | Partial OK |
|
||||
| `ccache/` | CCACHE_DIR | Yes (dir + env) |
|
||||
| `artifacts/` | Laptop handoff | Dummy file for smoke |
|
||||
| `logs/` | Bootstrap/preflight logs | Yes |
|
||||
| `secrets/` | Restricted placeholder | Empty; no keygen |
|
||||
|
||||
### GiteaInstance
|
||||
|
||||
| Field | Notes |
|
||||
|-------|-------|
|
||||
| Bind | Loopback / non-public |
|
||||
| Repos | `sunfish-a16-rom`, `sunfish-rom-manifest`, `sunfish-rom-build`, `sunfish-rom-patches` |
|
||||
| Canonical | Yes after first successful push of meta repo |
|
||||
|
||||
### GitMirror (off-VM)
|
||||
|
||||
| Field | Notes |
|
||||
|-------|-------|
|
||||
| Location | Laptop bare mirrors (initial) |
|
||||
| Direction | Laptop pulls (`clone --mirror` / `remote update`); no VM→laptop SSH |
|
||||
| Scope | All four project repos |
|
||||
| Success | At least one verified mirror cycle |
|
||||
|
||||
### AnsibleController / SemaphoreInstance
|
||||
|
||||
| Field | Notes |
|
||||
|-------|-------|
|
||||
| Host | `rom-dev01` |
|
||||
| First job | Environment smoke playbook |
|
||||
| Exposure | Same private access model as Gitea |
|
||||
|
||||
## Relationships
|
||||
|
||||
```text
|
||||
AdminSSH ──manages──► Host packages/systemd/firewall
|
||||
android-build ──writes──► /srv/rom (build-related trees)
|
||||
GiteaInstance ──hosts──► four Repositories
|
||||
Laptop ──mirrors──► Repositories (bare)
|
||||
SemaphoreInstance ──runs──► Ansible smoke playbook on Host
|
||||
Laptop Cursor ──RemoteSSH──► Host workspace
|
||||
Laptop ──rsync──► /srv/rom/artifacts (pull)
|
||||
```
|
||||
|
||||
## State transitions (meta Git)
|
||||
|
||||
1. `laptop-staging-only`
|
||||
2. `copied-to-host` (pre-Gitea)
|
||||
3. `gitea-canonical` (push complete)
|
||||
4. `mirrored-off-vm` (SC-007)
|
||||
|
||||
Do not destroy state (1) until (3) and (4) succeed.
|
||||
@@ -0,0 +1,476 @@
|
||||
# Implementation Plan: Development environment on rom-dev01
|
||||
|
||||
**Branch**: `001-development-environment` | **Date**: 2026-08-09 | **Spec**: [spec.md](./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)
|
||||
|
||||
```text
|
||||
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)
|
||||
|
||||
```text
|
||||
/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)
|
||||
|
||||
```text
|
||||
/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):
|
||||
|
||||
```text
|
||||
/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):
|
||||
|
||||
```text
|
||||
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.
|
||||
|
||||
```text
|
||||
# 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](./research.md) | Decisions: Gitea/Semaphore access, mirror, deps source, ccache |
|
||||
| [data-model.md](./data-model.md) | Operational entities only |
|
||||
| [quickstart.md](./quickstart.md) | Cold-start validation |
|
||||
| [contracts/](./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.
|
||||
@@ -0,0 +1,132 @@
|
||||
# Quickstart / validation: 001-development-environment
|
||||
|
||||
Use after `/speckit-implement` on a real `rom-dev01`. Until the VM exists, treat
|
||||
this as the acceptance script shape.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Host: `a15.ssystems.de` (`49.13.104.83`), SSH port **37037**, guest name `rom-dev01`
|
||||
- Laptop key: `~/.ssh/id_ed25519_rom_dev01` + Host aliases in
|
||||
`misc/config.d/a15-rom-dev01` (see `docs/001-host-runbook.md`)
|
||||
- Laptop has `rsync`, `ssh`, Cursor Remote SSH, `adb`/`fastboot`
|
||||
|
||||
## P1 validation path
|
||||
|
||||
### 1. Preflight captured
|
||||
|
||||
```bash
|
||||
ssh rom-dev01-root 'less /srv/rom/logs/preflight/preflight-latest.txt'
|
||||
```
|
||||
|
||||
### 2. Build user workspace
|
||||
|
||||
```bash
|
||||
ssh rom-dev01 'whoami; hostname; test -f ~/sunfish-a16-rom/specs/001-development-environment/spec.md && echo SPEC_OK'
|
||||
```
|
||||
|
||||
### 3. Toolchain smoke (clean shell)
|
||||
|
||||
```bash
|
||||
ssh rom-dev01 'bash -l -c "git --version; git lfs version; repo --version; python3 --version; ccache -s"'
|
||||
```
|
||||
|
||||
Expected: all commands succeed without sudo. See `/srv/rom/logs/toolchain-smoke.txt`.
|
||||
|
||||
### 4. Layout
|
||||
|
||||
```bash
|
||||
namei -l /srv/rom /srv/rom/artifacts /srv/rom/ccache
|
||||
touch /srv/rom/artifacts/.write_test && rm /srv/rom/artifacts/.write_test
|
||||
```
|
||||
|
||||
### 5. Meta repo present on host (pre-Gitea OK)
|
||||
|
||||
```bash
|
||||
test -d /home/android-build/sunfish-a16-rom/.git
|
||||
test -f /home/android-build/sunfish-a16-rom/specs/001-development-environment/spec.md
|
||||
```
|
||||
|
||||
### 6. Cursor Remote SSH
|
||||
|
||||
From laptop: open host workspace; edit a trivial file; confirm it is on the VM
|
||||
disk (not only local).
|
||||
|
||||
### 7. Artifact rsync smoke
|
||||
|
||||
On host:
|
||||
|
||||
```bash
|
||||
echo '001-smoke' > /srv/rom/artifacts/dummy-001.txt
|
||||
sha256sum /srv/rom/artifacts/dummy-001.txt
|
||||
```
|
||||
|
||||
On laptop:
|
||||
|
||||
```bash
|
||||
rsync -avz USER@ROM_DEV01:/srv/rom/artifacts/dummy-001.txt ./
|
||||
sha256sum ./dummy-001.txt # must match
|
||||
```
|
||||
|
||||
## P2 validation path
|
||||
|
||||
### 8. Gitea private access
|
||||
|
||||
```bash
|
||||
ssh -L 3000:127.0.0.1:3000 rom-dev01-root
|
||||
# browser: http://127.0.0.1:3000/ (user sunfish-admin; password in host /srv/rom/secrets/)
|
||||
```
|
||||
|
||||
Gitea binds **only** `127.0.0.1:3000`. Git over HTTP to that URL (no public `:2222`).
|
||||
|
||||
### 9. Four repos
|
||||
|
||||
In Gitea: `sunfish-a16-rom`, `sunfish-rom-manifest`, `sunfish-rom-build`,
|
||||
`sunfish-rom-patches` (under `sunfish-admin/`).
|
||||
|
||||
### 10. Ansible CLI smoke
|
||||
|
||||
```bash
|
||||
ssh rom-dev01 'cd ~/sunfish-rom-build && ansible-playbook -i ansible/inventory.ini ansible/smoke.yml'
|
||||
```
|
||||
|
||||
### 11. Semaphore smoke (unprivileged)
|
||||
|
||||
```bash
|
||||
ssh -L 3001:127.0.0.1:3001 rom-dev01-root
|
||||
# http://127.0.0.1:3001/ project sunfish-rom / template env-smoke
|
||||
```
|
||||
|
||||
Semaphore clones canonical `sunfish-rom-build` from Gitea and runs the smoke
|
||||
playbook without sudo. Evidence: `/srv/rom/logs/semaphore-smoke.txt`.
|
||||
|
||||
### 12. Off-VM mirrors
|
||||
|
||||
Laptop path: `/home/aventura/Archives/sunfish-rom-mirrors/*.git` (rsync from
|
||||
`/srv/rom/artifacts/gitea-mirrors/` on host).
|
||||
|
||||
```bash
|
||||
# refresh mirrors (host builds mirrors; laptop pulls via rsync — no token on laptop)
|
||||
rsync -a android-build@a15.ssystems.de:/srv/rom/artifacts/gitea-mirrors/ \
|
||||
~/Archives/sunfish-rom-mirrors/
|
||||
git -C ~/Archives/sunfish-rom-mirrors/sunfish-a16-rom.git fsck
|
||||
```
|
||||
|
||||
### 13. Restart survival
|
||||
|
||||
```bash
|
||||
ssh rom-dev01-root 'systemctl restart gitea semaphore; curl -fsS -o /dev/null http://127.0.0.1:3000/; curl -fsS -o /dev/null http://127.0.0.1:3001/'
|
||||
```
|
||||
|
||||
Evidence: `/srv/rom/logs/restart-smoke.txt`.
|
||||
|
||||
### 14. Isolation checklist
|
||||
|
||||
- No required internal NIC for workflow
|
||||
- Gitea/Semaphore binds only `127.0.0.1`
|
||||
- SSH still works after service restarts
|
||||
- Day-to-day docs do not require company VPN/internal DNS/LDAP/GitLab
|
||||
|
||||
## Done means
|
||||
|
||||
`001` complete → start **`002`** (AXP capture).
|
||||
Do **not** start Lineage `repo sync` / build as part of `001` success.
|
||||
@@ -0,0 +1,110 @@
|
||||
# Research: 001-development-environment
|
||||
|
||||
Phase 0 decisions for planning. Implementation may refine versions using live
|
||||
primary docs on `rom-dev01`.
|
||||
|
||||
## R1 — Gitea access model
|
||||
|
||||
**Decision**: Bind Gitea to loopback (or other non-public address) and access via
|
||||
SSH local port forward from the laptop. Do not require public HTTPS exposure.
|
||||
|
||||
**Rationale**: Constitution/`001` require private project Git without unnecessary
|
||||
Internet exposure; single developer; SSH already required for admin/Remote SSH.
|
||||
|
||||
**Alternatives considered**:
|
||||
- Public HTTPS + firewall allowlist — rejected for `001` (more moving parts;
|
||||
accidental exposure risk).
|
||||
- VPN-only bind — rejected as a *required* path (may exist externally; project
|
||||
must not depend on company VPN).
|
||||
|
||||
## R2 — Semaphore access model
|
||||
|
||||
**Decision**: Same as Gitea — loopback/non-public + SSH tunnel. First job is an
|
||||
**unprivileged** localhost/environment smoke playbook only (facts + path/tool
|
||||
asserts). No sudo credentials in Semaphore for `001` smoke.
|
||||
|
||||
**Rationale**: Semaphore is orchestration UI, not a public portal; Android build
|
||||
pipeline is explicitly out of scope for `001`; avoid baking privileged secrets
|
||||
into the first integration.
|
||||
|
||||
**Alternatives considered**: Public Semaphore — rejected. Full ROM pipeline in
|
||||
Semaphore — deferred past `003` manual first build. Privileged playbooks —
|
||||
later explicit decision.
|
||||
|
||||
## R3 — Off-VM Git history backup
|
||||
|
||||
**Decision**: Laptop-initiated **bare mirrors** (`git clone --mirror` /
|
||||
`git remote update`) of the four Gitea repos for `001` (SC-007). Laptop pulls;
|
||||
VM does not SSH into the laptop. Borg/full Gitea DR later.
|
||||
|
||||
**Rationale**: Spec requires off-VM copy of project Git history, not a complete
|
||||
backup platform. Pull-from-laptop avoids opening inbound access on the laptop.
|
||||
|
||||
**Alternatives considered**: Borg-only — optional later. Push-from-VM to laptop
|
||||
— rejected (would require laptop inbound SSH).
|
||||
|
||||
## R4 — Lineage/AOSP host dependencies
|
||||
|
||||
**Decision**: At implement time, follow **current primary LineageOS / AOSP host
|
||||
build documentation** for Ubuntu; record installed package set in logs. Do not
|
||||
freeze a long package list in the plan.
|
||||
|
||||
**Rationale**: Dependency lists drift; stale pins cause false confidence.
|
||||
|
||||
**Alternatives considered**: Copy a historical package list from blogs —
|
||||
rejected.
|
||||
|
||||
## R5 — `repo` installation source
|
||||
|
||||
**Decision**: Install Google's `repo` launcher via the method recommended by
|
||||
current Lineage/AOSP docs (typically curl to `~/bin/repo` or distro package if
|
||||
still adequate); verify `repo --version` as `android-build`.
|
||||
|
||||
**Rationale**: Align with upstream bring-up docs used for `003`.
|
||||
|
||||
## R6 — ccache policy vs build parallelism
|
||||
|
||||
**Decision**: `001` configures `CCACHE_DIR=/srv/rom/ccache` (or equivalent),
|
||||
ownership for `android-build`, and a disk size cap. Document host capacity
|
||||
(6 vCPU / 52 GB / 32 GB swap). **Defer `-j` / OOM tuning to `003`.**
|
||||
|
||||
**Rationale**: ccache is storage/env; parallel link/compile jobs belong to the
|
||||
first real build feature.
|
||||
|
||||
**Alternatives considered**: Encoding `-j$(nproc)` defaults in `001` — rejected
|
||||
as premature and confusing with FR-007 (document capacity; don't run builds).
|
||||
|
||||
## R7 — Meta-repo bootstrap before Gitea
|
||||
|
||||
**Decision**: Copy laptop staging Git tree to host path
|
||||
`/home/android-build/sunfish-a16-rom` first (rsync/scp/bundle preserving
|
||||
`.git`), install Gitea, create repos, push, then mark Gitea canonical. Keep
|
||||
laptop staging until mirror verified. Ansible/automation lives in
|
||||
`sunfish-rom-build`, not in the meta Spec Kit tree.
|
||||
|
||||
**Rationale**: Avoid chicken/egg `git clone` against non-existent Gitea; keep
|
||||
Cursor edit path single and clear.
|
||||
|
||||
## R7b — Gitea Git transport vs OpenSSH base
|
||||
|
||||
**Decision**: UI via loopback + SSH tunnel. Exact Git clone/push transport
|
||||
chosen at implement after host inspection (tunneled HTTP and/or tunneled Git
|
||||
SSH are valid). Do not assume a public secondary SSH port for Gitea; do not
|
||||
blindly edit `saltenv=base`-managed sshd.
|
||||
|
||||
## R8 — Coexistence with `saltenv=base`
|
||||
|
||||
**Decision**: Preflight + stop/report on conflicts with base-managed config.
|
||||
Project Ansible may manage **project** paths/services; must not silently rewrite
|
||||
Salt-owned system policy.
|
||||
|
||||
**Rationale**: Host is dual-touched (SSYSTEMS base then project). Fighting Salt
|
||||
causes flapping.
|
||||
|
||||
## R9 — data-model / contracts
|
||||
|
||||
**Decision**: No application domain model or HTTP API contracts for `001`.
|
||||
Operational entities only in `data-model.md`. `contracts/` marked N/A.
|
||||
|
||||
**Rationale**: Infrastructure bootstrap feature; inventing APIs would be template
|
||||
noise.
|
||||
@@ -0,0 +1,230 @@
|
||||
# Feature Specification: Development environment on rom-dev01
|
||||
|
||||
**Feature Branch**: `001-development-environment`
|
||||
|
||||
**Created**: 2026-08-09
|
||||
|
||||
**Status**: Draft
|
||||
|
||||
**Input**: Provision the ROM development environment on an already-existing
|
||||
`rom-dev01` host and establish the laptop ↔ VM ↔ Pixel workflow defined by the
|
||||
project constitution.
|
||||
|
||||
## Context / non-goals
|
||||
|
||||
**Assumed already true before this feature starts:**
|
||||
|
||||
- `rom-dev01` exists (Ubuntu Server 24.04 LTS);
|
||||
- one reachable public IPv4 address (public NIC only);
|
||||
- SSH administration works;
|
||||
- SSYSTEMS base provisioning (`saltenv=base`) is complete.
|
||||
|
||||
**Explicitly out of scope:**
|
||||
|
||||
- Proxmox / `pm-test01` VM creation;
|
||||
- public subnet/IP selection;
|
||||
- company Salt states, Redmine, or SSYSTEMS governance;
|
||||
- LineageOS source sync or first ROM build (see `003`);
|
||||
- AXP capture (see `002`).
|
||||
|
||||
**Isolation posture (in scope for guest config/docs):**
|
||||
|
||||
- Keep the host usable without SSYSTEMS internal network services.
|
||||
- Do not add an internal NIC from this feature.
|
||||
- Bind Gitea/Semaphore so they need not be world-exposed.
|
||||
- Document guest firewall / egress expectations (block or avoid unnecessary
|
||||
traffic to SSYSTEMS internal ranges; SSH from laptop must work).
|
||||
- Sharing the public VLAN with lab/gateway VMs is acceptable.
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - Reachable ROM build host workspace (Priority: P1)
|
||||
|
||||
As the developer, I can SSH from my laptop into `rom-dev01` and use a dedicated
|
||||
`android-build` account with a documented home/workspace layout for Android
|
||||
work.
|
||||
|
||||
**Why this priority**: Without a stable host account and layout, no later build
|
||||
feature can proceed.
|
||||
|
||||
**Independent Test**: SSH as the project build user, create a file under the
|
||||
documented workspace, confirm ownership and paths.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** `rom-dev01` is up, **When** I connect via SSH from the laptop,
|
||||
**Then** authentication succeeds without requiring this project to change
|
||||
PVE/network provisioning.
|
||||
2. **Given** SSH works, **When** the `android-build` account is provisioned,
|
||||
**Then** it exists with a documented home and build-related directories.
|
||||
3. **Given** the account exists, **When** I inspect `/srv/rom` (or the agreed
|
||||
layout), **Then** directories for sources, artifacts, logs, and tooling are
|
||||
present with clear ownership.
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Android build toolchain prerequisites (Priority: P1)
|
||||
|
||||
As the developer, the host has the packages and tools needed to sync and build
|
||||
LineageOS for sunfish (repo, git, git-lfs, JDK, build deps, ccache), sized for
|
||||
6 vCPU / 52 GB RAM / 32 GB swap.
|
||||
|
||||
**Why this priority**: Vanilla LOS build (`003`) depends on this.
|
||||
|
||||
**Independent Test**: `repo`/`git`/`ccache` respond; documented package set is
|
||||
installed; ccache directory exists and is writable by `android-build`.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** `android-build` session, **When** I run `repo --version` and
|
||||
`git --version`, **Then** both succeed.
|
||||
2. **Given** build dependencies are installed, **When** I check ccache config
|
||||
and cache dir, **Then** ccache is usable by `android-build`.
|
||||
3. **Given** constrained host resources, **When** build parallelism defaults are
|
||||
documented, **Then** they are conservative (not assuming 16 vCPU / 64 GB).
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - Project Git and orchestration services (Priority: P2)
|
||||
|
||||
As the developer, private Gitea and Semaphore (over Ansible) run on `rom-dev01`
|
||||
for project repos and ROM workflow orchestration, without exposing those UIs
|
||||
unnecessarily to the public Internet. Project Git history must not exist only
|
||||
on the build VM.
|
||||
|
||||
**Why this priority**: Needed before multi-repo ROM automation; can follow basic
|
||||
toolchain if sequenced. Losing `out/` is fine; losing specs/patches/history is
|
||||
not.
|
||||
|
||||
**Independent Test**: Gitea serves locally/over SSH tunnel; initial repos
|
||||
`sunfish-a16-rom`, `sunfish-rom-manifest`, `sunfish-rom-build`,
|
||||
`sunfish-rom-patches` exist or are documented to create; Semaphore can run a
|
||||
trivial Ansible playbook; off-VM backup/mirror procedure documented and proven
|
||||
once.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** Gitea is installed, **When** I access it via the approved path
|
||||
(SSH tunnel or existing access policy), **Then** I can create/browse private
|
||||
project repositories.
|
||||
2. **Given** Ansible is installed, **When** Semaphore is configured, **Then** it
|
||||
drives Ansible without hiding undocumented build steps.
|
||||
3. **Given** public IP exists, **When** reviewing listening services, **Then**
|
||||
Gitea/Semaphore are not required to be open to the whole Internet for normal
|
||||
operation.
|
||||
4. **Given** the four project repositories, **When** backup/mirror is configured,
|
||||
**Then** an off-VM copy exists (laptop clone, Borg, or other agreed target)
|
||||
so `rom-dev01` is not the only copy of project history.
|
||||
|
||||
---
|
||||
|
||||
### User Story 4 - Guest isolation from SSYSTEMS internals (Priority: P2)
|
||||
|
||||
As the developer, normal ROM work on `rom-dev01` does not require company
|
||||
internal DNS, VPN, LDAP, GitLab, NFS, or Zabbix, and project UIs are not
|
||||
exposed more than necessary.
|
||||
|
||||
**Why this priority**: Limits blast radius and keeps the personal project
|
||||
logically separate even though the hypervisor is shared lab hardware.
|
||||
|
||||
**Independent Test**: From the VM, confirm no required route/NIC into internal
|
||||
SSYSTEMS networks for the workflow; Gitea/Semaphore reachable via approved
|
||||
non-public path; checklist documents firewall/DNS posture.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** the provisioned guest, **When** I inspect network interfaces,
|
||||
**Then** only the public NIC is required for this project's workflow.
|
||||
2. **Given** Gitea and Semaphore are installed, **When** I review listen
|
||||
addresses / access docs, **Then** they are not required to be open to the
|
||||
whole Internet.
|
||||
3. **Given** guest firewall/DNS docs, **When** I follow them, **Then** day-to-day
|
||||
ROM work does not depend on SSYSTEMS internal resolvers or VPN.
|
||||
|
||||
---
|
||||
|
||||
### User Story 5 - Laptop remains a thin client (Priority: P2)
|
||||
|
||||
As the developer, Cursor Remote SSH uses `rom-dev01` as the heavy workspace;
|
||||
the Pixel stays USB-attached to the laptop; artifacts are `rsync`'d for
|
||||
flashing.
|
||||
|
||||
**Why this priority**: Enforces the disk/space strategy and flashing model.
|
||||
|
||||
**Independent Test**: Documented Remote SSH workflow; sample artifact path
|
||||
`/srv/rom/artifacts` syncs to the laptop with `rsync`.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** Cursor on the laptop, **When** I open the project via Remote SSH,
|
||||
**Then** I edit/build on `rom-dev01` without keeping `.repo`/`out` on the
|
||||
laptop.
|
||||
2. **Given** a dummy artifact in `/srv/rom/artifacts`, **When** I `rsync` it to
|
||||
the laptop, **Then** the file arrives for local `adb`/`fastboot` use.
|
||||
3. **Given** the Pixel is attached to the laptop, **When** following the
|
||||
workflow docs, **Then** no USB passthrough to the VM is required.
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- Host reboots: services and mount/layout expectations are documented.
|
||||
- Disk pressure on 2 TB: cleanup conventions for `out/` and old artifacts.
|
||||
- SSH admin user ≠ `android-build`: project must not hard-depend on laptop
|
||||
usernames (`aventura` / `ubuntu`).
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: Provide dedicated `android-build` account and documented workspace.
|
||||
- **FR-002**: Install Android/LineageOS build prerequisites and ccache.
|
||||
- **FR-003**: Create `/srv/rom` layout (sources, artifacts, logs, tooling) or
|
||||
document an equivalent and use it consistently.
|
||||
- **FR-004**: Install and configure private Gitea for project repositories,
|
||||
including meta repo `sunfish-a16-rom` plus `sunfish-rom-manifest`,
|
||||
`sunfish-rom-build`, and `sunfish-rom-patches`.
|
||||
- **FR-005**: Install Ansible; configure Semaphore as UI over Ansible for ROM
|
||||
workflow (initially may only prove a smoke playbook).
|
||||
- **FR-006**: Document Cursor Remote SSH + `rsync` artifact + laptop
|
||||
adb/fastboot workflow.
|
||||
- **FR-007**: Document conservative build parallelism defaults for 6 vCPU /
|
||||
52 GB / swap 32 GB.
|
||||
- **FR-008**: Ensure Spec Kit project for `sunfish-a16-rom` is available on the
|
||||
host (push/clone the meta repo from this staging tree or Gitea once published).
|
||||
- **FR-009**: Document and apply guest isolation posture: public-NIC-only
|
||||
workflow; no required dependency on SSYSTEMS internal DNS/VPN/LDAP/GitLab/NFS/
|
||||
Zabbix; Gitea/Semaphore not world-exposed by default; guest firewall guidance
|
||||
to avoid unnecessary traffic to internal SSYSTEMS ranges (without encoding
|
||||
concrete company IPs as project secrets/config in Git unless necessary).
|
||||
- **FR-010**: Provide an off-VM backup or mirror of project-owned Gitea
|
||||
repositories so the build VM is not the only copy of project history
|
||||
(mechanism may be laptop clones, Borg, or another agreed target).
|
||||
|
||||
### Key Entities
|
||||
|
||||
- **rom-dev01**: pre-existing Ubuntu 24.04 build host.
|
||||
- **android-build**: project build account.
|
||||
- **/srv/rom**: canonical on-host data layout.
|
||||
- **Gitea / Semaphore / Ansible**: project-owned services on the VM.
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: From a clean SSH session as `android-build`, toolchain commands
|
||||
required for `003` are available without further ad-hoc package hunting.
|
||||
- **SC-002**: `/srv/rom/artifacts` (or documented equivalent) is writable and
|
||||
used as the artifact handoff point to the laptop.
|
||||
- **SC-003**: Gitea hosts (or is ready to host) the **four** initial project
|
||||
repos (`sunfish-a16-rom`, `sunfish-rom-manifest`, `sunfish-rom-build`,
|
||||
`sunfish-rom-patches`) without using company GitLab.
|
||||
- **SC-004**: Written workflow doc allows a cold start: SSH → workspace →
|
||||
(later) build → rsync → flash on laptop-attached Pixel.
|
||||
- **SC-005**: No playbook or doc in this feature creates or reconfigures the
|
||||
Proxmox VM itself.
|
||||
- **SC-006**: Isolation checklist passes: no internal NIC required; project UIs
|
||||
accessible without public Internet exposure; workflow docs do not require
|
||||
SSYSTEMS VPN/internal DNS for normal use.
|
||||
- **SC-007**: Off-VM backup/mirror of the four Gitea project repos is documented
|
||||
and demonstrated at least once.
|
||||
@@ -0,0 +1,208 @@
|
||||
# Tasks: Development environment on rom-dev01
|
||||
|
||||
**Input**: Design documents from `/specs/001-development-environment/`
|
||||
|
||||
**Prerequisites**: plan.md (required), spec.md, research.md, data-model.md,
|
||||
quickstart.md, contracts/ (N/A)
|
||||
|
||||
**Tests**: Observational verify steps embedded in tasks (no separate TDD suite).
|
||||
|
||||
**Organization**: Setup → Foundational (preflight) → User stories by priority →
|
||||
Polish. **Do not run these tasks until `rom-dev01` SSH works.** Generating this
|
||||
file is documentation-only.
|
||||
|
||||
**Canonical paths**:
|
||||
- Meta Spec Kit tree: `/home/android-build/sunfish-a16-rom`
|
||||
- Data root: `/srv/rom/`
|
||||
- Automation repo (after Gitea): `sunfish-rom-build` (not the meta repo)
|
||||
|
||||
## Format: `[ID] [P?] [Story] Description`
|
||||
|
||||
- **[P]**: Can run in parallel (different files, no incomplete dependencies)
|
||||
- **[Story]**: US1…US5 from spec.md
|
||||
- Every task includes a path and an observable verify hint
|
||||
|
||||
## Path Conventions
|
||||
|
||||
Host paths are absolute on `rom-dev01`. Docs/logs also land under
|
||||
`/home/android-build/sunfish-a16-rom/docs/` or `/srv/rom/logs/` as noted.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Setup (docs readiness — can be done on laptop before VM)
|
||||
|
||||
- [x] T001 Confirm active feature is `specs/001-development-environment` via `.specify/feature.json` and that constitution is `0.1.7` in `.specify/memory/constitution.md`
|
||||
- [x] T002 [P] Create host-facing doc stub `docs/001-host-runbook.md` in meta repo (preflight results, paths, Gitea tunnel notes — fill during implement)
|
||||
- [x] T003 [P] Create FR/SC evidence checklist stub `docs/001-verification-matrix.md` mapping each FR-*/SC-* from `specs/001-development-environment/spec.md` to blank evidence fields
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (blocking — host preflight before stories)
|
||||
|
||||
**Goal**: Observe `rom-dev01` without fighting `saltenv=base`; abort on conflict.
|
||||
|
||||
- [x] T004 On `rom-dev01` as admin, capture preflight baseline into `/srv/rom/logs/preflight/` (create `/srv/rom/logs` with safe perms if needed): Ubuntu/kernel, CPU/RAM/swap, disk free, interfaces/routes, users, firewall summary (**do not flush**), listening ports, apt sources → also summarize in `docs/001-host-runbook.md`
|
||||
- [x] T005 Record coexistence rule in `docs/001-host-runbook.md`: if base-managed config conflicts with project needs, **stop and report**; do not silently replace Salt-managed settings
|
||||
- [x] T006 Verify SSH admin path still works after any read-only inspection; note rollback contacts/commands in `docs/001-host-runbook.md`
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 — Reachable build workspace (P1)
|
||||
|
||||
**Story goal**: `android-build` exists with SSH key login from laptop; `/srv/rom` owned correctly; meta tree at fixed path.
|
||||
|
||||
**Independent test**: `ssh android-build@rom-dev01` from laptop; `/srv/rom/artifacts` writable; `/home/android-build/sunfish-a16-rom` contains Spec Kit tree (after T013).
|
||||
|
||||
### Implementation
|
||||
|
||||
- [x] T007 [US1] Create unprivileged user `android-build` with home `/home/android-build` (admin); document uid/gid in `docs/001-host-runbook.md`
|
||||
- [x] T008 [US1] Configure SSH public-key access for `android-build` using the developer's existing public key only (do not copy private keys; do not modify global sshd/`saltenv=base` policy unless required). Verify from the laptop that `ssh android-build@rom-dev01` succeeds. Document Cursor `Host` entry (`User android-build`) in `docs/001-host-runbook.md`
|
||||
- [x] T009 [US1] Create `/srv/rom/{src,references,tooling,ccache,artifacts,logs,secrets}` with ownership for `android-build` on writable trees; `secrets/` restricted/empty — document modes in `docs/001-host-runbook.md`
|
||||
- [x] T010 [US1] Verify: `namei -l /srv/rom /srv/rom/artifacts` and `sudo -iu android-build touch /srv/rom/artifacts/.write_test` succeeds; no `chmod 777`
|
||||
- [x] T011 [US1] Document account model (admin vs `android-build` vs service users) in `docs/001-host-runbook.md` — never build Android as root
|
||||
- [x] T012 [US1] Copy laptop staging Git tree to `/home/android-build/sunfish-a16-rom` preserving `.git` (rsync/scp/bundle); verify `test -f /home/android-build/sunfish-a16-rom/specs/001-development-environment/spec.md`
|
||||
- [x] T013 [US1] Ensure `/srv/rom/tooling` is **not** used as Spec Kit checkout; note distinction in `docs/001-host-runbook.md`
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 — Android host toolchain (P1)
|
||||
|
||||
**Story goal**: Clean-login toolchain ready for later `003` (no `repo sync` / build).
|
||||
|
||||
**Independent test**: `ssh android-build@rom-dev01` → `git`, `git lfs`, `repo`, `python3`, `ccache -s`, and LOS-doc Java/prereqs succeed.
|
||||
|
||||
### Implementation
|
||||
|
||||
- [x] T014 [US2] As admin, install host packages per **current** primary LineageOS/AOSP Ubuntu build documentation; record exact package list in `/srv/rom/logs/toolchain-packages.txt`
|
||||
- [x] T015 [US2] Install/configure `repo` for `android-build` per current docs; verify `ssh android-build@rom-dev01 'repo --version'`
|
||||
- [x] T016 [US2] Ensure `git` + `git-lfs` for `android-build`; verify `ssh android-build@rom-dev01 'git lfs version'`
|
||||
- [x] T017 [US2] Configure ccache: `CCACHE_DIR=/srv/rom/ccache`, ownership `android-build`, size cap; document env in `/home/android-build/.bashrc` or `/home/android-build/.profile` and `docs/001-host-runbook.md`
|
||||
- [x] T018 [US2] Verify: `ssh android-build@rom-dev01 'ccache -s'` shows configured cache dir
|
||||
- [x] T019 [US2] Document FR-007 capacity (6 vCPU / 52 GB / 32 GB swap) and **defer `-j`/OOM tuning to `003`** in `docs/001-host-runbook.md` — do not run a Lineage build
|
||||
- [x] T020 [US2] Clean-shell toolchain smoke checklist executed; paste command outputs into `/srv/rom/logs/toolchain-smoke.txt` and tick FR-002/SC-001 in `docs/001-verification-matrix.md`
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 5 — Laptop thin client (P1/P2 overlap — do after US1–US2)
|
||||
|
||||
**Story goal**: Cursor Remote SSH as `android-build` + artifact rsync proven; Pixel stays on laptop USB.
|
||||
|
||||
**Independent test**: Remote SSH edit on host as `android-build`; dummy artifact SHA256 matches after rsync; laptop has `adb`/`fastboot` binaries.
|
||||
|
||||
### Implementation
|
||||
|
||||
- [x] T021 [US5] Document Cursor Remote SSH → `/home/android-build/sunfish-a16-rom` with `User android-build` in `docs/001-host-runbook.md` and update `specs/001-development-environment/quickstart.md` if needed
|
||||
- [x] T022 [US5] Create `/srv/rom/artifacts/dummy-001.txt` as `android-build`; record `sha256sum` in `/srv/rom/logs/rsync-smoke.txt`
|
||||
- [x] T023 [US5] From laptop, `rsync` dummy artifact; verify SHA256 match; record laptop path in `/srv/rom/logs/rsync-smoke.txt` and `docs/001-verification-matrix.md` (SC-002)
|
||||
- [x] T024 [US5] On laptop only: run `adb --version` and `fastboot --version` (do **not** connect or flash the Pixel); record versions in `docs/001-host-runbook.md`. Document adb/fastboot-on-laptop / no USB passthrough rule (SC-004 partial)
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: User Story 3 — Gitea, Ansible, Semaphore, off-VM mirror (P2)
|
||||
|
||||
**Story goal**: Private Gitea with four repos; canonical meta remote; `sunfish-rom-build` committed+pushed; laptop pull mirrors; unprivileged Semaphore smoke from canonical repo.
|
||||
|
||||
**Independent test**: Four repos on Gitea; meta history pushed; `sunfish-rom-build` fetch shows smoke playbook commit; laptop `git clone --mirror` works; Semaphore runs unprivileged smoke.
|
||||
|
||||
### Implementation
|
||||
|
||||
- [x] T025 [US3] Install Gitea on `rom-dev01` with systemd + persistent data; bind UI to loopback/non-public; document unit/paths in `docs/001-host-runbook.md`
|
||||
- [x] T026 [US3] Store Gitea admin credentials only in restricted host location under `/srv/rom/secrets/` or service-native secret path — **never** commit to Git; document location (not secrets) in `docs/001-host-runbook.md`
|
||||
- [x] T027 [US3] Verify: local `curl` to Gitea UI succeeds; confirm not relying on public Internet exposure; record bind address in `docs/001-host-runbook.md`
|
||||
- [x] T028 [US3] Document Git transport chosen after host inspection (HTTP via SSH tunnel and/or tunneled Git SSH); **do not** open public `:2222` or blindly edit `saltenv=base` sshd — write choice in `docs/001-host-runbook.md`
|
||||
- [x] T029 [US3] Create Gitea repos: `sunfish-a16-rom`, `sunfish-rom-manifest`, `sunfish-rom-build`, `sunfish-rom-patches`
|
||||
- [x] T030 [US3] Push `/home/android-build/sunfish-a16-rom` history to Gitea `sunfish-a16-rom`; set `origin` to Gitea; verify remote in that directory
|
||||
- [x] T031 [US3] Seed minimal structure in a working tree of `sunfish-rom-build` for Ansible (e.g. `ansible/` playbooks path) — **not** inside Spec Kit meta repo
|
||||
- [x] T032 [US3] Install Ansible on `rom-dev01`; document version in `docs/001-host-runbook.md`
|
||||
- [x] T033 [US3] Add unprivileged smoke playbook under `sunfish-rom-build` (gather_facts + asserts for `/srv/rom`, `android-build`, toolchain); **no become/sudo secrets**
|
||||
- [x] T034 [US3] Run smoke playbook successfully via `ansible-playbook` CLI; log output to `/srv/rom/logs/ansible-smoke.txt`
|
||||
- [x] T035 [US3] Commit and push the initial `sunfish-rom-build` structure and smoke playbook to its canonical Gitea repository; verify the pushed commit is visible from a fresh fetch/clone (document clone URL/path in `docs/001-host-runbook.md`)
|
||||
- [x] T036 [US3] Install Semaphore; bind like Gitea (loopback/tunnel); no public exposure; document in `docs/001-host-runbook.md`
|
||||
- [x] T037 [US3] Wire Semaphore so it executes the smoke playbook **originating from the canonical `sunfish-rom-build` repository**, using the simplest private mechanism supported by the actual installation (token/HTTP/SSH — decide at implement). Run once successfully; evidence in `/srv/rom/logs/semaphore-smoke.txt` (FR-005). Still unprivileged / no sudo password in Semaphore
|
||||
- [x] T038 [US3] From **laptop**, create bare mirrors with `git clone --mirror` for all four repos (laptop initiates connection); run `git remote update` + `git fsck`; document mirror paths in `docs/001-host-runbook.md` (FR-010/SC-007)
|
||||
- [x] T039 [US3] Confirm laptop staging `/home/aventura/Workspaces/sunfish-a16-rom` is **retained** until T030+T035+T038 verified; only then optionally mark staging as non-canonical in `docs/001-host-runbook.md`
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: User Story 4 — Guest isolation (P2)
|
||||
|
||||
**Story goal**: Isolation checklist passes without breaking SSH.
|
||||
|
||||
**Independent test**: Public-NIC-only workflow; UIs non-public; SSH still works; no VPN/internal DNS required in docs.
|
||||
|
||||
### Implementation
|
||||
|
||||
- [x] T040 [US4] Document interfaces/routes: project workflow requires only public NIC — `docs/001-host-runbook.md` + evidence in `docs/001-verification-matrix.md` (SC-006)
|
||||
- [x] T041 [US4] Apply isolation/firewall guidance **additively** if needed: never flush nftables/iptables; never close active SSH before tested alternative; record before/after in `/srv/rom/logs/firewall-notes.txt`
|
||||
- [x] T042 [US4] Re-verify Gitea/Semaphore binds remain non-public; SSH admin + `android-build` + tunnel still work — update `docs/001-verification-matrix.md` FR-009/SC-006
|
||||
- [x] T043 [US4] Confirm day-to-day runbook does not require SSYSTEMS VPN/internal DNS/LDAP/GitLab/NFS/Zabbix — `docs/001-host-runbook.md`
|
||||
|
||||
---
|
||||
|
||||
## Phase 8: Polish & cross-cutting
|
||||
|
||||
- [x] T044 [P] Fill all FR-*/SC-* evidence rows in `docs/001-verification-matrix.md` (including SC-005: no PVE/qm/Salt VM tasks were performed)
|
||||
- [x] T045 [P] Align `specs/001-development-environment/quickstart.md` with final ports/paths/tunnel commands actually used
|
||||
- [x] T046 Restart Gitea (+ Semaphore) via systemd; confirm recovery; optional reboot when safe; note results in `/srv/rom/logs/restart-smoke.txt`
|
||||
- [x] T047 Declare handoff in `docs/001-host-runbook.md`: **`001` complete → proceed to `002`**, do not start Lineage sync/build
|
||||
- [x] T048 Verify that no credentials/tokens/private keys are present in the **current trees OR committed history** of `sunfish-a16-rom` and `sunfish-rom-build`. Review current files plus reachable Git history; if a secret was ever committed, remove it from history and rotate/revoke it before closing `001`. Method may be manual/`git log -p`/`git rev-list` review (no mandatory Gitleaks/TruffleHog)
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & story order
|
||||
|
||||
```text
|
||||
Phase 1 (laptop docs)
|
||||
↓
|
||||
Phase 2 preflight (host)
|
||||
↓
|
||||
US1 user + SSH key + /srv/rom + meta copy
|
||||
↓
|
||||
US2 toolchain ──────────────┐
|
||||
↓ │
|
||||
US5 thin-client smokes ←───┘ (needs artifacts + meta path + android-build SSH)
|
||||
↓
|
||||
US3 Gitea → repos → Ansible in sunfish-rom-build → commit/push → Semaphore → laptop mirrors
|
||||
↓
|
||||
US4 isolation hardening/validation
|
||||
↓
|
||||
Polish / handoff → STOP until implement; next feature 002 after 001 done
|
||||
```
|
||||
|
||||
**MVP (minimum useful host)**: complete through US2 + US5 (T007–T024) even if Gitea delayed — but **formal `001` done** requires P2 (US3+US4+polish) per plan.
|
||||
|
||||
## Parallel opportunities
|
||||
|
||||
- T002/T003 on laptop before VM
|
||||
- After T009: T011 docs can parallel T012 copy if disk ready
|
||||
- T014–T016 package/`repo`/`git-lfs` partly serial (apt) then T017 ccache
|
||||
- T044/T045 polish docs in parallel after evidence exists
|
||||
|
||||
## FR/SC coverage map
|
||||
|
||||
| ID | Tasks |
|
||||
|----|-------|
|
||||
| FR-001 | T007–T011, T008 |
|
||||
| FR-002 | T014–T018, T020 |
|
||||
| FR-003 | T009–T010, T013 |
|
||||
| FR-004 | T025–T030 |
|
||||
| FR-005 | T031–T037 |
|
||||
| FR-006 | T021–T024 |
|
||||
| FR-007 | T019 |
|
||||
| FR-008 | T012, T030 |
|
||||
| FR-009 | T040–T043 |
|
||||
| FR-010 | T038–T039 |
|
||||
| SC-001 | T020 |
|
||||
| SC-002 | T022–T023 |
|
||||
| SC-003 | T029–T030 |
|
||||
| SC-004 | T021, T024, T047 |
|
||||
| SC-005 | T005, T044 |
|
||||
| SC-006 | T040–T043 |
|
||||
| SC-007 | T038 |
|
||||
|
||||
## Implementation strategy
|
||||
|
||||
1. Do **not** execute host tasks until SSH to `rom-dev01` works.
|
||||
2. After analyze approval: STOP until VM exists, then `/speckit-implement`.
|
||||
3. Follow P1 then P2; stop/report on Salt conflicts.
|
||||
4. Do not plan `002` until `001` implement + verification complete.
|
||||
Reference in new issue
Block a user