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:
sunfish-admin committed 2026-09-16 21:56:22 +00:00
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.
+476
View File
@@ -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.
+230
View File
@@ -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.
+208
View File
@@ -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.
@@ -0,0 +1,3 @@
# Contracts: N/A for 002
No HTTP/API contracts. Device observation + documentation only.
@@ -0,0 +1,30 @@
# Data model: 002-axp-reference-capture
Operational entities only (no application domain model).
## DeviceRole
| Field | Notes |
|-------|-------|
| role | `GOLDEN` \| `LAB` |
| serial_or_label | Measured; may be redacted in public docs |
| bootloader_state | Measured (`fastboot getvar unlocked` / UI) |
| build_fingerprint | From `getprop` |
| flash_policy | GOLDEN=forbidden; LAB=allowed |
## GoldenInventory
Essential package/config rows: name, version, cert fingerprint (short), path,
notes, `re-query` flag.
## BankingFlow
Ordered steps 1–8 from spec; redacted (no passwords/OTPs/PII).
## LabRescueBundle
Factory zip path on host, SHA-256, Google source URL, flash steps, wipe warning.
## ReferenceArchive
Host path `/srv/rom/references/axp/archive/` — sunfish-scoped AXP materials.
+112
View File
@@ -0,0 +1,112 @@
# Implementation Plan: AXP.OS GOLDEN reference capture
**Branch**: `002-axp-reference-capture` | **Date**: 2026-09-13 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `/specs/002-axp-reference-capture/spec.md`
## Summary
Identify GOLDEN vs LAB sunfish, write LAB Google factory-image emergency
rescue, capture essential live GOLDEN AXP inventory + banking flow (redacted),
and store raw artifacts under `/srv/rom/references/axp/` on `rom-dev01` while
committing only redacted docs to `sunfish-a16-rom`. Parallel: rsync existing
laptop AXP archive per `docs/axp-sunfish-preservation.md` (does not replace
live `adb`).
## Technical Context
**Language/Version**: Bash + `adb`/`fastboot` inventory scripts; Markdown docs
**Primary Dependencies**: OpenSSH to `rom-dev01`; Android platform-tools on
laptop; optional wget/curl for factory images on host
**Storage**: `/srv/rom/references/axp/` (raw); Git = redacted summaries only
**Testing**: Observational checklists (getprop/dumpsys excerpts); no unit suite
**Target Platform**: Two physical sunfish + `rom-dev01` Ubuntu 24.04
**Project Type**: Device documentation / reference capture
**Performance Goals**: N/A — correctness and non-invention of device facts
**Constraints**: Never flash GOLDEN; no secrets/APKs in Git; NeoBackup not a gate;
bootloader state measured not assumed; gaps marked “re-query GOLDEN”
**Scale/Scope**: One developer; essential inventory for `004`–`006` hard-gates
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- Independence from SSYSTEMS: yes (Gitea/host paths only)
- GOLDEN do-not-flash: enforced in docs + FR-006
- No Magisk-as-production-path invention: Magisk recorded as GOLDEN oracle only
- Secrets/APKs off Git: FR-005 / SC-005
- Prefer primary sources: Google factory images page; live `adb` over blogs
**Post-design**: Still pass — plan does not invent package versions.
## Project Structure
### Documentation (this feature)
```text
specs/002-axp-reference-capture/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/ # N/A (README only)
└── tasks.md
```
### Source / artifact layout
```text
# In-repo (redacted)
docs/device-roles.md # update serials/labels + lock notes
docs/002-golden-inventory.md # essential inventory + banking flow
docs/002-lab-stock-rescue.md # LAB factory rescue
docs/axp-pro-parity.md # cross-links only (non-normative)
# On rom-dev01 (raw; not Git)
/srv/rom/references/axp/
├── archive/ # rsync from ~/Archives/AXP.OS
├── golden/ # adb pulls, APK copies, dumpsys (restricted)
├── lab/ # factory image zip + checksums
└── logs/ # capture session logs
```
## Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| — | — | — |
## Phases
### P0 — Docs + archive seed (no phone required)
1. Freeze plan/research/tasks.
2. Rsync laptop `~/Archives/AXP.OS` → `/srv/rom/references/axp/archive/`.
3. Draft LAB stock rescue from current Google sunfish factory image listing;
download image+hash to `/srv/rom/references/axp/lab/` when network allows.
### P1 — Device roles + measured identity (phones required)
1. Connect GOLDEN and LAB; record serials/labels; `getprop` + fastboot lock.
2. Update `docs/device-roles.md` + `docs/002-golden-inventory.md` stubs with
measured facts only.
### P2 — Essential GOLDEN inventory + banking flow
1. Scripted `adb` collection into `/srv/rom/references/axp/golden/` (redact
before any Git paste).
2. Write banking install order from GOLDEN observation.
3. Cross-link parity matrix rows that become known.
### Gate before `003`
LAB rescue doc reviewed; GOLDEN explicitly do-not-flash; FR-006 satisfied.
**No** Lineage sync in this feature.
@@ -0,0 +1,21 @@
# Quickstart: 002-axp-reference-capture
## Prerequisites
- `001` done (`rom-dev01`, `android-build`, `/srv/rom`)
- Laptop: `adb` / `fastboot`
- Both phones labeled (or serials ready)
## Path
1. Confirm no device attached confusion: `adb devices -l`
2. Capture GOLDEN then LAB (or vice versa) — never flash GOLDEN
3. Store raw under `/srv/rom/references/axp/golden/` (rsync from laptop pulls)
4. Update redacted `docs/002-golden-inventory.md` + banking flow
5. Verify LAB rescue zip + hash on host before any `003` flash
## Gate before 003
- [ ] GOLDEN marked do-not-flash in docs
- [ ] LAB factory rescue doc + image hash present
- [ ] Essential inventory + banking flow (gaps = re-query, not invented)
@@ -0,0 +1,47 @@
# Research: 002-axp-reference-capture
## R1 — Live GOLDEN vs offline AXP archive
**Decision**: Treat live GOLDEN `adb` as the compatibility oracle. Offline archive
under `/srv/rom/references/axp/archive/` is supporting reference (docs/git/
images), never a substitute for measured device state.
**Rationale**: Spec FR-003/SC-003; constitution two-device model.
## R2 — NeoBackup / wipe GOLDEN
**Decision**: Out of scope / not a gate (spec + `docs/device-roles.md`).
## R3 — LAB factory rescue source
**Decision (measured 2026-09-13)**: Target build `TQ3A.230805.001.S1` /
`sunfish-tq3a.230805.001.s1-factory-82902ea9.zip`, SHA-256
`82902ea98c80f1a22706279a30d2bc46aa55cbe60703c9a02ba043bd052c6d99`.
Google CDN URL for that zip now returns **404**; sunfish section on
`developers.google.com/android/images` is effectively pruned. Download from
Internet Archive item `sunfish-tq3a.230805.001.s1` and **verify SHA-256** against
the historical Google/Calyx hash before treating it as a rescue image.
**Alternatives**: Other community mirrors only if IA fails; same hash required.
## R4 — Raw artifact location
**Decision**: `/srv/rom/references/axp/{archive,golden,lab,logs}/` on
`rom-dev01`. Git holds redacted Markdown only.
## R5 — Inventory depth
**Decision**: Essential checklist from spec US2; missing items marked
`re-query GOLDEN` — never invent versions/UIDs.
## R6 — Magisk on GOLDEN
**Decision**: Record Magisk/DenyList as **oracle facts**. Project production
root path remains KernelSU-Next in later features; Magisk is not promoted as
our stage.
## R7 — Parallelism with archive rsync
**Decision**: Start laptop→host AXP archive rsync as soon as `001` host is
ready; does not block writing LAB rescue docs; does not wait for phone `adb`.
+183
View File
@@ -0,0 +1,183 @@
# Feature Specification: AXP.OS GOLDEN reference capture
**Feature Branch**: `002-axp-reference-capture`
**Created**: 2026-08-09
**Status**: P1 complete (gate to `003`); archive rsync may still finish in background
**Input**: Document and sample the **GOLDEN** daily-driver AXP.OS sunfish as a
live compatibility oracle; label GOLDEN vs LAB; prepare **LAB** stock factory
rescue. Experimental flashing stays on LAB only (`docs/device-roles.md`).
## Context / non-goals
- There are **two** sunfish devices: **GOLDEN** (**AXP.OS 22.2 / Android 15**
daily driver — do not experiment/flash) and **LAB** (flash/reflash freely).
- Exact GOLDEN build fingerprint/patch is **measured** here (`getprop`). GOLDEN
is AXP 22.2/A15 — **not** the last GrapheneOS sunfish release (that was
**A13**, historical reference only).
- Broader offline AXP *sunfish* archive (website, selected git, images) is
documented in `docs/axp-sunfish-preservation.md` for execution on `rom-dev01`
— not a full Disroot org clone; does not replace live GOLDEN `adb`.
- Capture **essential** versions/config up front; GOLDEN remains available for
later live `adb` re-queries — do not require an exhaustive one-shot dump of
everything before `003`.
- **NeoBackup / wipe / migrate GOLDEN are not project requirements.**
- Do **not** invent package versions/UIDs in the constitution; this feature
records what GOLDEN actually runs.
- Do not commit credentials, tokens, or personal identifiers to Git.
- Observe devices; do **not** assume bootloader unlock state until measured
(both GOLDEN and LAB).
- Product intent vs AXP Pro lives in non-normative `docs/axp-pro-parity.md`.
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Device roles + LAB stock rescue (Priority: P1)
As the developer, GOLDEN and LAB are clearly identified, and LAB has a written
Google factory-image emergency rescue so experiments cannot permanently brick
the lab phone.
**Why this priority**: Prevents flashing the wrong phone; keeps LAB recoverable.
**Independent Test**: Serials/labels documented; factory image procedure for LAB
reviewed; GOLDEN marked do-not-flash.
**Acceptance Scenarios**:
1. **Given** two sunfish devices, **When** I record serials (or equivalent IDs)
and physical/doc labels, **Then** GOLDEN vs LAB cannot be confused.
2. **Given** GOLDEN and LAB, **When** I inspect bootloader/fastboot/`getprop`
on each, **Then** lock state and build identity are recorded without prior
assumptions.
3. **Given** official Google sunfish factory images, **When** I document image
set, hashes, and flash steps for **LAB**, **Then** LAB emergency stock rescue
exists before `003` experiments.
4. **Given** project docs, **When** reviewed, **Then** they state GOLDEN must
not receive experimental project flashes.
---
### User Story 2 - Essential GOLDEN inventory (Priority: P1)
As the developer, I have enough structured inventory of GOLDEN AXP to start
`004`–`006`, knowing I can re-probe GOLDEN later for missing details.
**Why this priority**: Defines the live oracle without blocking on perfection.
**Independent Test**: Essential checklist complete or explicitly deferred with
“re-query GOLDEN later” notes — no silent unknowns for hard-gates.
**Acceptance Scenarios**:
1. **Given** GOLDEN AXP, **When** I collect `getprop` / fingerprints, **Then**
exact ROM/build identifiers are stored.
2. **Given** microG, **When** inventoried at essential level, **Then** recorded:
version, signing cert; Self-Check; Device Registration; Cloud Messaging;
relevant permissions.
3. **Given** Phonesky / `com.android.vending`, **When** inventoried, **Then**
recorded: version; APK path; cert; permissions; spoofing state; useful
`dumpsys package` / priv-app XML notes.
4. **Given** Insular, **When** inventoried, **Then** recorded: profile IDs;
profile owner; `dumpsys device_policy` excerpt; owner vs work UIDs/packages;
Yuh/Swissquote locations.
5. **Given** Magisk/root, **When** inventoried, **Then** recorded: Magisk
version; modules; **DenyList including processes**.
6. **Given** GSF Proxy and related packages, **When** inventoried, **Then**
versions/paths/signatures are recorded.
7. **Given** banking interest, **When** measurable, **Then** record observational
Play Integrity (or equivalent) state plus Yuh/Swissquote pass/fail on GOLDEN
— comparative baseline only, not a Strong Integrity promise for our ROM.
8. **Given** Daily-driver MVP features we will replace natively, **When**
capturing modules/settings, **Then** document current call-recording and
FLAG_SECURE bypass behaviour/source on GOLDEN (AXP vs module; global/per-app).
---
### User Story 3 - Golden banking flow on GOLDEN (Priority: P1)
As the developer, the known-good banking install order is written from GOLDEN
so `006` can reproduce it on LAB (with KernelSU substituting DenyList later).
**Why this priority**: Order is load-bearing.
**Independent Test**: Step list matches GOLDEN procedure.
**Acceptance Scenarios**:
1. **Given** GOLDEN, **When** I document the banking app install flow, **Then**
it explicitly includes:
1. Aurora Store → install
2. **Do not open**
3. Magisk DenyList **before** first process (include processes)
4. Clone into Insular
5. First launch inside Insular
6. Phonesky/Play in Insular
7. Play update inside work profile if required
8. Verify login/biometrics/push/reboot as applicable
2. **Given** that flow, **When** stored in the redacted summary, **Then** it
omits passwords, OTPs, and personal identifiers.
---
### User Story 4 - Safe artifact handling (Priority: P2)
As the developer, sensitive captures stay off Git; redacted summaries are
committed to `sunfish-a16-rom`.
**Acceptance Scenarios**:
1. **Given** raw pulls/APKs, **When** stored, **Then** they live under e.g.
`/srv/rom/references/axp/` on `rom-dev01`, not in Git.
2. **Given** in-repo summary, **When** reviewed, **Then** it has versions/hashes/
workflow steps without personal secrets.
---
### Edge Cases
- GOLDEN encrypt/PIN: document without storing the PIN.
- Missing detail later: re-attach GOLDEN and `adb` — do not invent.
- LAB brick: use LAB stock factory rescue; do not wipe GOLDEN to “recover”.
- Optional personal NeoBackup of GOLDEN is outside project gates.
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: Label and record GOLDEN vs LAB identities; measure bootloader/
fastboot state on both.
- **FR-002**: Document LAB Google factory-image emergency rescue (hashes + steps).
- **FR-003**: Capture essential GOLDEN inventory (microG, GSF, Phonesky, Insular,
Magisk/DenyList processes, Yuh/Swissquote, call-recording + FLAG_SECURE
sources, observational Play Integrity if measurable).
- **FR-004**: Document GOLDEN banking flow (Aurora → NO OPEN → DenyList →
Insular → Play update).
- **FR-005**: Store raw artifacts off-Git; commit only redacted docs.
- **FR-006**: Gate: no experimental flash on LAB until LAB rescue docs exist and
GOLDEN is explicitly marked do-not-flash; **no** NeoBackup/wipe of GOLDEN
required.
- **FR-007**: Cross-link useful facts into `docs/axp-pro-parity.md` /
`docs/device-roles.md` without inventing unseen features.
### Key Entities
- **GOLDEN**: live AXP oracle (not wiped for bring-up).
- **LAB**: experimental flash target + stock rescue docs.
- **Reference bundle**: host-local capture directory.
- **Redacted summary**: in-repo documentation.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: GOLDEN vs LAB clearly identified; agents/docs forbid flashing GOLDEN.
- **SC-002**: LAB stock factory rescue written and reviewed before `003`.
- **SC-003**: Essential GOLDEN inventory + banking flow complete enough for
`006` hard-gates; gaps marked “re-query GOLDEN” not invented.
- **SC-004**: Yuh/Swissquote verified working on GOLDEN and documented.
- **SC-005**: Git has no proprietary APK dumps or personal credential material.
- **SC-006**: Bootloader states in docs match measured GOLDEN and LAB reality.
+59
View File
@@ -0,0 +1,59 @@
# Tasks: AXP.OS GOLDEN reference capture
**Input**: Design docs from `/specs/002-axp-reference-capture/`
**Prerequisites**: `001` complete on `rom-dev01`
## Phase 1: Setup (no phone)
- [x] T001 Confirm active feature `specs/002-axp-reference-capture` in `.specify/feature.json`
- [x] T002 [P] Create `docs/002-lab-stock-rescue.md` stub (LAB only; Google factory source)
- [x] T003 [P] Create `docs/002-golden-inventory.md` stub (essential checklist + banking flow placeholders)
- [x] T004 [P] Create host dirs `/srv/rom/references/axp/{archive,golden,lab,logs}` as `android-build`
## Phase 2: Offline AXP archive seed (no phone)
- [~] T005 Rsync `~/Archives/AXP.OS/` → host archive *(~18G/38G in progress; not a `003` flash gate)*
- [x] T006 Record archive size + SNAPSHOT_DATE in `/srv/rom/references/axp/logs/archive-rsync.txt` *(partial; refresh when rsync ends)*
## Phase 3: LAB stock rescue (P1 — may download without phone)
- [x] T007 Resolve current official sunfish factory image URL + SHA-256 from Google primary page (or document pruning if only one remains) *(Google CDN 404; hash + IA fallback documented)*
- [x] T008 Download factory zip to `/srv/rom/references/axp/lab/`; verify SHA-256 *(VERIFY_OK 2026-09-13)*
- [x] T009 Complete `docs/002-lab-stock-rescue.md` with flash-all steps, wipe warning, LAB-only scope
- [x] T010 Cross-check FR-002/SC-002 in a short verification note in that doc
## Phase 4: Device roles measured (P1 — phones required)
- [x] T011 Connect GOLDEN (AXP A15 main); record serial + `getprop` into golden inventory (raw log on host) — **adb sample only, no modifications** *(serial `11071JEC203839`)*
- [x] T012 Connect LAB; record serial/label + build identity *(serial `11231JEC207477`, iodé/LOS 7.8 A16 — see `docs/002-lab-iodes-baseline.md`)*
- [x] T013 Measure bootloader/fastboot unlock state on **both**; update `docs/device-roles.md` + inventory (SC-006) *(GOLDEN unlocked/orange; LAB locked/green — via getprop)*
- [x] T014 Explicit “do not flash GOLDEN” callout in `docs/device-roles.md` and inventory (SC-001)
## Phase 5: Essential GOLDEN inventory (P1 — GOLDEN adb)
- [x] T015 Capture microG essential fields (version, cert, Self-Check notes) *(version + sig short; Self-Check UI = re-query)*
- [x] T016 Capture Phonesky / `com.android.vending` essentials *(Phonesky_AXP-OS 50.2.12)*
- [x] T017 Capture Insular / work profile essentials (`dumpsys device_policy` excerpt redacted) *(Island 6.4.2, user 10)*
- [x] T018 Capture Magisk version, modules, DenyList **including processes** *(Magia UI DenyList packages from screenshots; Magisk 29.0; process expand partial; modules dir not readable without root)*
- [x] T019 Capture GSF Proxy / related packages versions/paths/signatures
- [x] T020 Record observational Play Integrity + Yuh/Swissquote pass/fail on GOLDEN (SC-004) *(Swissquote PASS; Yuh FAIL HA / certificate invalid; Finsky PI token path still completes; keystore2 TEE errors in log)*
- [x] T021 Document call-recording + FLAG_SECURE bypass source on GOLDEN *(deferred — re-query; not blocking `003`)*
- [x] T022 Mark any missing rows `re-query GOLDEN` — do not invent
## Phase 6: Banking flow (P1)
- [x] T023 Write redacted GOLDEN banking install order / observed layout in `docs/002-golden-inventory.md`
- [x] T024 Confirm no passwords/OTPs/PII in Git docs (SC-005)
## Phase 7: Polish / gate
- [x] T025 Cross-link known facts into `docs/axp-pro-parity.md` without inventing features (FR-007)
- [x] T026 FR/SC checklist pass for gate: LAB rescue ready; GOLDEN do-not-flash; inventory+flow sufficient for `006` *(see `docs/002-gate-handoff.md`)*
- [x] T027 Handoff note: **`002` complete → `003` on LAB only**
- [x] T028 Verify Git trees contain no proprietary APK dumps or credential files
## Parallel opportunities
- T002–T004 parallel; T005 archive rsync while drafting T007–T009
- T011–T014 require physical devices; block inventory until done
@@ -0,0 +1,3 @@
# Contracts: N/A for 003
No HTTP/API contracts. Build/flash/smoke documentation only.
@@ -0,0 +1,27 @@
# Data model: 003-lineage-23-vanilla-build
## VanillaBuild
| Field | Notes |
|-------|-------|
| manifest_url / branch | From `repo init` |
| resolved_manifest | `repo manifest -r` artifact |
| lunch_target | e.g. `lineage_sunfish-userdebug` (confirm at build) |
| build_id / date | Host timestamp |
| artifact_paths | img/zip under `/srv/rom/artifacts/003-…` |
| sha256 | Per artifact |
| build_log | Compressed under `/srv/rom/logs` |
## LabFlashSession
| Field | Notes |
|-------|-------|
| serial | Must be LAB `11231JEC207477` |
| bootloader_state_before/after | Measured |
| flash_steps | Documented |
| boot_ok | bool |
| getprop_fingerprint | Post-boot |
## SmokeChecklist
P1 + extended rows from spec; each `pass` / `fail` / `skip` + evidence.
@@ -0,0 +1,80 @@
# Implementation Plan: LineageOS 23.2 vanilla build for sunfish
**Branch**: `003-lineage-23-vanilla-build` | **Date**: 2026-09-13 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `/specs/003-lineage-23-vanilla-build/spec.md`
## Summary
On `rom-dev01`, sync official LineageOS **23.2** for `sunfish`, build
`userdebug` (test-keys), publish hashed artifacts under `/srv/rom/artifacts`,
`rsync` to the laptop, flash **LAB only** (`11231JEC207477`), and run P1 +
extended hardware smoke. No microG, KernelSU, Magisk, or hardening patches.
On flash failure, use `docs/002-lab-stock-rescue.md` — never touch GOLDEN.
## Technical Context
**Language/Version**: Bash + Lineage `repo` / Soong / ninja (AOSP Android 16)
**Primary Dependencies**: LineageOS 23.2 manifest; device/kernel trees for
sunfish; `ccache`; OpenSSH; laptop `adb`/`fastboot`
**Storage**: `/srv/rom/src` (tree), `/srv/rom/ccache`, `/srv/rom/artifacts`,
`/srv/rom/logs` on `rom-dev01`
**Testing**: Observational boot + hardware checklists (documented results)
**Target Platform**: `rom-dev01` Ubuntu 24.04 build; LAB Pixel 4a flash
**Project Type**: ROM bring-up / reproducible baseline artifact
**Performance Goals**: Complete sync+build with conservative `-j`; survive
OOM via documented recovery
**Constraints**: LAB only; no project patches beyond LOS device needs; no
Magisk/KSU “just to test”; GOLDEN forbidden
**Scale/Scope**: One developer; one known-good vanilla baseline for regression
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- Independence from SSYSTEMS: yes
- GOLDEN do-not-flash: enforced (FR-007)
- Functionality-first vanilla base before hardening: yes
- Secrets/APKs off Git: artifacts stay on host/laptop paths; Git = docs/metadata
- Prefer primary sources: Lineage wiki + official manifest
**Post-design**: Pass — plan does not invent device trees or flash GOLDEN.
## Project Structure
### Documentation (this feature)
```text
specs/003-lineage-23-vanilla-build/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
└── tasks.md
```
### Source / artifact layout
```text
# rom-dev01
/srv/rom/src/lineage/ # repo workspace (or documented path)
/srv/rom/artifacts/003-*/ # images, zip, SHA-256, repo manifest -r
/srv/rom/logs/003-*/ # compressed build logs
# laptop
~/…/artifacts/003-*/ # rsync target for flash
docs/003-*.md # flash + smoke results (redacted)
```
## Complexity Tracking
None — single vanilla bring-up path.
@@ -0,0 +1,27 @@
# Quickstart: 003 vanilla LOS 23.2
## Prerequisites
- `001` host ready (`rom-dev01`, `android-build`, ccache, repo)
- `002` gate: LAB rescue zip OK; GOLDEN do-not-flash
- USB to **LAB** only for flash
## Build (host)
```bash
ssh rom-dev01 # as android-build
# repo init/sync lineage-23.2 sunfish — exact commands in tasks / build notes
# breakfast/lunch sunfish userdebug
# mka bacon or documented target; log + hash artifacts into /srv/rom/artifacts
```
## Flash (laptop → LAB)
1. `adb devices` / `fastboot devices` — confirm LAB serial
2. Unlock LAB if needed (wipes device)
3. Flash per LOS wiki / project `docs/003-flash-lab.md` (to be written)
4. If brick/bootloop: `docs/002-lab-stock-rescue.md`
## Smoke
Fill `docs/003-smoke-lab.md` P1 + extended; keep as known-good baseline before `004`.
@@ -0,0 +1,51 @@
# Research: 003-lineage-23-vanilla-build
## R1 — Manifest / branch
**Decision**: Use official LineageOS `lineage-23.2` (or the wiki-documented
sunfish 23.2 branch name measured at implement time). Record exact
`repo init` URL/branch and `repo manifest -r` for the known-good build.
**Rationale**: Constitution base = LOS 23.2 / A16; no AXP/GOS base.
## R2 — Build user / path
**Decision**: Build as `android-build` on `rom-dev01`; tree under
`/srv/rom/src/` (exact subdir documented at init); `CCACHE_DIR=/srv/rom/ccache`.
## R3 — Variant
**Decision**: `userdebug` + LOS default/test-keys for this stage (FR-002).
Release keys / AVB = later (`011`).
## R4 — Flash target
**Decision**: **LAB only** (`11231JEC207477`). Owner approved unlock/wipe for
first custom flash. GOLDEN never flashed. Factory zip on disk =
**emergency rescue only** — owner does not want stock as a planned LAB OS;
skip “Yuh on stock” experiment.
## R4b — Banking / root / Phonesky (owner 2026-09-13)
- Hard-gate focus: **Yuh** (+ Swissquote as tracked); Revolut not a hard-gate.
- Root: **KernelSU-Next + SusFS**; ReSukiSU only if KSU-Next fails on LAB.
- Play: **AXP-style patched Phonesky/GPlay** priv-app (not XDA FakeStore-first).
## R5 — Parallelism / OOM
**Decision**: Start conservative `-j` (e.g. half of vCPUs); document failure
recovery (`out/` cleanup) in build notes. Measure host: ~8 vCPU / 62 GiB RAM.
## R6 — What “vanilla” excludes
**Decision**: No microG, Phonesky, signature-spoof patches, KernelSU/SUSFS,
Magisk, call-recording/FLAG_SECURE ports, Graphene/Divest hardening — even if
present on LAB’s current iodé image (that baseline will be wiped by flash).
## R8 — Proprietary blobs
**Decision (owner 2026-09-13):** Factory/Lineage-equivalent blob base, then
selective **AXP sunfish** diffs and **GrapheneOS** diffs (historical sunfish A13
+ transferable bits from a currently supported Pixel). No exotic blob hunting
for GPS; do not overthink for `003` vanilla (factory-equivalent only).
+183
View File
@@ -0,0 +1,183 @@
# Feature Specification: LineageOS 23.2 vanilla build for sunfish
**Feature Branch**: `003-lineage-23-vanilla-build`
**Created**: 2026-08-09
**Status**: Active — implementation next (vanilla sync/build on `rom-dev01`)
**Input**: Produce the first vanilla LineageOS 23.2 / Android 16 build for
`sunfish`, copy images to the laptop, flash, and verify boot plus basic
hardware — with no microG, KernelSU, or hardening yet.
## Context / non-goals
**Prerequisites:**
- `001-development-environment` sufficiently complete to sync/build;
- `002-axp-reference-capture` P1 complete (GOLDEN labeled; LAB stock rescue
documented). Flash **LAB only** — never GOLDEN (`docs/device-roles.md`).
**Out of scope for this feature:**
- microG / Phonesky / signature spoofing;
- KernelSU / SUSFS / Magisk parity;
- call recording, FLAG_SECURE toggles, privacy extras;
- project signing / AVB / OTA implementation;
- Graphene/AXP/Divest patch ports.
Build target: **`userdebug`** with development/test-keys.
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Reproducible vanilla sync & build (Priority: P1)
As the developer, I can sync LineageOS 23.2 for sunfish on `rom-dev01` and
complete a build that emits flashable images, with manifest/revisions recorded.
**Why this priority**: Establishes the real base ROM.
**Independent Test**: Build finishes; artifact paths listed; manifest revision
document stored under `/srv/rom` with hashes.
**Acceptance Scenarios**:
1. **Given** `android-build` on `rom-dev01`, **When** I sync the LOS 23.2
sunfish source via `repo`, **Then** the tree matches a documented manifest
revision.
2. **Given** the synced tree, **When** I build `userdebug` for sunfish with
conservative parallelism, **Then** the build completes without requiring
project patches beyond LOS-needed device/kernel trees.
3. **Given** a successful build, **When** I collect outputs, **Then** images/
zip needed to flash sunfish are in `/srv/rom/artifacts` (or documented path)
with SHA-256 hashes and a compressed build log.
---
### User Story 2 - Flash from laptop (Priority: P1)
As the developer, I `rsync` artifacts to the laptop and flash the Pixel 4a over
USB using fastboot/adb, following the recovery notes from `002` if something
fails.
**Why this priority**: Closes the loop on the thin-client workflow.
**Independent Test**: Device boots the new build; `getprop` shows LineageOS /
expected fingerprints (not AXP).
**Acceptance Scenarios**:
1. **Given** artifacts on the laptop, **When** I flash per documented steps,
**Then** the device boots into the vanilla LOS build.
2. **Given** a failed flash/boot on **LAB**, **When** I follow `002` LAB stock
factory rescue docs, **Then** LAB returns to usable stock. GOLDEN is not
touched.
---
### User Story 3 - Basic hardware smoke test (Priority: P1)
As the developer, after first boot I verify essential radios and peripherals
well enough to trust this build as the clean base for later features — and I
know what was already broken on **vanilla** LOS before we add patches.
**Why this priority**: A boot-only “success” that breaks telephony/Wi-Fi is not
a usable base. Later regressions must be attributable.
**Independent Test**: Checklist results stored for P1 and extended items on the
vanilla build.
**Acceptance Scenarios**:
1. **Given** first boot completed, **When** I run the **P1 smoke** set, **Then**
each item either works or is logged as a blocking defect with evidence:
- encryption / PIN
- SELinux mode (`Enforcing` expected)
- Android security patch level
- vendor security patch level / build baseline
- SIM detection
- mobile data
- phone calls
- SMS (send/receive as applicable)
- Wi-Fi
- Bluetooth
- GPS
- camera(s)
- audio (speaker/mic)
- NFC
- sensors (basic)
- fingerprint
- USB data / ADB
- charging
2. **Given** P1 smoke done, **When** I run the **extended baseline** set,
**Then** each is recorded (need not all be P1 blockers, but must not be
skipped silently):
- airplane mode
- flashlight
- vibration
- screen rotation
- USB OTG (if hardware/cable available)
- headphone jack
- WebView implementation / basic page load
- app sideload install smoke (non-Google)
- Widevine / DRM security level
- eSIM capability/state, if available
- WireGuard kernel capability/baseline
- bootloader version
- baseband / modem version
3. **Given** smoke results, **When** deciding to proceed to `004`, **Then** no
unresolved P1 hardware blockers remain (or they are explicitly accepted with
rationale).
---
### Edge Cases
- Build disk/oom with 52 GB + swap: document job `-j` and failure recovery
(`out/` cleanup).
- Partial artifact set: do not flash until required partitions/images are
present and hashed.
- Do not install Magisk/KernelSU “just to test” in this feature.
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: Initialize from the documented official LineageOS 23.2
manifest/branch (`repo init` → sync) and capture a revision-locked resolved
manifest (e.g. `repo manifest -r`) for every known-good build.
- **FR-002**: Build `userdebug` images with test-keys / default LOS signing for
this stage.
- **FR-003**: Record manifest, revisions, build variant, config, hashes, and
compressed log for the successful build.
- **FR-004**: Publish artifacts to `/srv/rom/artifacts` and document `rsync` +
flash procedure.
- **FR-005**: Boot sunfish on the vanilla build and run the P1 + extended
hardware/baseline checklist (record results even for non-blockers).
- **FR-006**: Keep tree free of microG/Phonesky/KernelSU/hardening patches
intended for later features.
- **FR-007**: On LAB flash failure, follow `002` LAB stock factory rescue; never
flash or wipe GOLDEN as part of recovery.
### Key Entities
- **Vanilla LOS build**: clean base artifact set.
- **Build metadata**: manifest + hashes + log bundle.
- **Smoke checklist**: hardware/regression minimum for this stage.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: One successful sunfish `userdebug` build exists with recorded
metadata and hashes.
- **SC-002**: Device boots that build after laptop-side flash.
- **SC-003**: P1 and extended hardware/baseline checklists are completed and
stored (extended items may be non-blocking but must be recorded).
- **SC-004**: No dependency on LSPosed/Magisk modules for this stage’s success.
- **SC-005**: This build is tagged/kept as a known-good flashable baseline for
regression when later features break the device.
@@ -0,0 +1,54 @@
# Tasks: LineageOS 23.2 vanilla build for sunfish
**Input**: Design docs from `/specs/003-lineage-23-vanilla-build/`
**Prerequisites**: `001` done; `002` P1 gate (`docs/002-gate-handoff.md`)
## Phase 1: Prep
- [x] T001 Confirm active feature `specs/003-lineage-23-vanilla-build`
- [x] T002 [P] Create `docs/003-build-notes.md` stub (manifest URL/branch TBD at init)
- [x] T003 [P] Create `docs/003-flash-lab.md` stub (LAB serial check; link rescue)
- [x] T004 [P] Create `docs/003-smoke-lab.md` checklist from spec P1 + extended
- [x] T005 Verify LAB serial still `11231JEC207477`; confirm GOLDEN not attached before any flash *(LAB serial documented; flash later)*
- [x] T006 Confirm host dirs `/srv/rom/{src,artifacts,logs,ccache}` writable by `android-build`
## Phase 2: Sync
- [x] T007 `repo init` official LineageOS 23.2 (wiki-verified URL/branch) under `/srv/rom/src/…` *(`/srv/rom/src/lineage`, `lineage-23.2`)*
- [x] T008 `repo sync` sunfish device/kernel dependencies; record wall time / failures *(finished 2026-09-13T21:27:26Z exit=0; tree ~180G)*
- [x] T009 Save `repo manifest -r` to `/srv/rom/artifacts/003-…/manifest-rev.xml`
## Phase 3: Build
- [x] T010 Configure ccache env; choose parallelism (**`-j8`** = match vCPU)
- [x] T011 `lunch`/`breakfast` sunfish `userdebug`; start build; capture log
- [x] T012 On OOM/disk failure: document recovery; retry (no silent `-j` guessing) *(N/A — no OOM; mid-run bump -j4→-j8)*
- [x] T013 Collect flashable images/zip into `/srv/rom/artifacts/003-…`; SHA-256 all
- [x] T014 Compress build log beside artifacts; write build metadata (variant, date, host)
## Phase 4: Flash LAB
- [x] T015 `rsync` artifacts to laptop *(`${WORKSPACE}/.local-artifacts/003-vanilla/20260914-lineage-23.2-sunfish/`)*
- [x] T016 Unlock LAB bootloader if still locked (owner-approved wipe) — **LAB only**
- [x] T017 Flash per `docs/003-flash-lab.md`; verify serial before every `fastboot` command
- [x] T018 Confirm first boot; `getprop` shows LOS vanilla (not AXP / not prior iodé id)
- [ ] T019 If fail: factory rescue from `002`; do not touch GOLDEN *(N/A so far)*
## Phase 5: Smoke
- [x] T020 Complete P1 smoke checklist in `docs/003-smoke-lab.md`
- [x] T021 Complete extended baseline checklist (record skips with reason)
- [x] T022 Decide gate to `004`: no unresolved P1 blockers (or explicit accept)
## Phase 6: Polish
- [x] T023 Tag/keep known-good baseline note (paths + hashes) for regression
(`docs/003-gate-handoff.md` + artifacts dir)
- [x] T024 Confirm tree has no microG/KSU/hardening patches (FR-006)
(vanilla identity props; no microG packages on LAB pre-004)
- [x] T025 Handoff: **`003` complete → `004`** (microG path) on LAB only
## Parallel opportunities
- T002–T004 while T006 host checks run
- T015 rsync can start as soon as T013 hashes exist
@@ -0,0 +1,35 @@
# Specification Quality Checklist: microG + Phonesky
**Purpose**: Validate specification completeness before planning
**Created**: 2026-09-14
**Feature**: [spec.md](../spec.md)
## Content Quality
- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed
## Requirement Completeness
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified
## Feature Readiness
- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification
## Notes
- Spoofing deferred to `005` by design; Self-Check may be partially blocked until then.
- Phonesky signing strategy is FR-010 (document during plan/implement; keys host-only).
@@ -0,0 +1,35 @@
# Contract: system packages (004)
## Packages that MUST be present on LAB after flash
| Package | Type | Notes |
|---------|------|-------|
| `com.google.android.gms` | system/priv | microG |
| `com.google.android.gsf` | system/priv | GsfProxy |
| `com.android.vending` | **priv-app** | Patched Phonesky |
## Packages that MUST NOT be present
| Package | Notes |
|---------|-------|
| FakeStore (typical `com.android.vending` stub from partner_gms) | Conflicts with Phonesky |
| Magisk / KernelSU manager (as ROM-preinstalled) | Out of scope until `007` |
## Host path contract (rom-dev01)
| Path | Contents | In Git? |
|------|----------|---------|
| `/srv/rom/prebuilts/microg/` | GmsCore + GsfProxy APKs + `VERSIONS.txt` | No |
| `/srv/rom/prebuilts/phonesky/` | Phonesky APK + permission XMLs + `VERSIONS.txt` | No |
| `/srv/rom/secrets/phonesky-signing/` | Optional project keys | No |
| Meta-repo `docs/004-*.md` | Runbook + self-check | Yes |
| Product/vendor mk under LOS tree | References prebuilts by path | Tree on host; mirror patches in meta if small |
## Verification commands (LAB)
```bash
adb -s 11231JEC207477 shell pm path com.google.android.gms
adb -s 11231JEC207477 shell pm path com.google.android.gsf
adb -s 11231JEC207477 shell pm path com.android.vending
adb -s 11231JEC207477 shell pm list packages | grep -i fakestore || true
```
@@ -0,0 +1,56 @@
# Data model: 004 packages
## Entities
### MicroGGmsCore
| Field | Description |
|-------|-------------|
| package | `com.google.android.gms` |
| role | microG Services Core |
| install | privileged / system (product or system_ext per chosen mk) |
| source | Official microG release APK on host prebuilts |
| golden_ref | `0.3.15.250932` (AXP inventory) |
### GsfProxy
| Field | Description |
|-------|-------------|
| package | `com.google.android.gsf` |
| role | Legacy GSF / C2DM proxy |
| install | system app or priv-app |
| source | microG GsfProxy release (or rebuilt for targetSdk if needed) |
| golden_ref | `GsfProxy_GH` v0.1.0 on GOLDEN |
### PhoneskyPatched
| Field | Description |
|-------|-------------|
| package | `com.android.vending` |
| role | Patched Play Store |
| install | **priv-app** + privapp-permissions + default-permissions XML |
| source | AXP archive APK (not Git) |
| golden_ref | Phonesky_AXP-OS **50.2.12-24** |
| first_candidate | archive **50.3.27-24** or **51.4.19-24** |
### PermissionConfig
| Artifact | Purpose |
|----------|---------|
| `privapp-permissions-com.android.vending.xml` | Privileged permissions allowlist |
| `com.android.vending.xml` | Feature/permission declarations as needed |
| `phonesky-permissions.xml` (default-permissions) | Default grants |
### ForbiddenCompanion
| package | rule |
|---------|------|
| FakeStore / conflicting Companion | MUST NOT ship alongside Phonesky |
## Relationships
- Phonesky **replaces** FakeStore in the compatibility stack.
- microG Self-Check may require spoofing of GmsCore (and later Phonesky) —
spoofing policy owned by `005`.
- All binaries → host prebuilts → product makefile → image; evidence →
`/srv/rom/artifacts/004-…`.
@@ -0,0 +1,89 @@
# Implementation Plan: microG + GSF Proxy + patched Phonesky
**Branch**: `004-microg-phonesky-integration` | **Date**: 2026-09-14 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `/specs/004-microg-phonesky-integration/spec.md`
## Summary
Add the Google compatibility stack to the sunfish LOS 23.2 / Android 16 tree on
**LAB only**: microG GmsCore, GSF Proxy, and **patched Phonesky** as priv-app
(AXP-style), without FakeStore, Magisk, or KernelSU. Signature spoofing beyond
what LOS already provides stays in `005`. Proprietary APKs live only under
`/srv/rom/` (never Git).
## Technical Context
**Language/Version**: Android.mk / Soong (`Android.bp` where needed); bash
runbooks on `rom-dev01`
**Primary Dependencies**: LineageOS 23.2 tree (`/srv/rom/src/lineage`); microG
release APKs; AXP `packages_apps_phonesky` permission XMLs + signed APK from
project AXP archive; LOS restricted signature spoofing (baseline)
**Storage**: Host paths `/srv/rom/prebuilts/{microg,phonesky}/`, secrets under
`/srv/rom/secrets/`
**Testing**: LAB flash + `pm`/`dumpsys` + microG Self-Check screenshots; no
unit-test harness required
**Target Platform**: Pixel 4a (`sunfish`) LAB `11231JEC207477`; Android 16
**Project Type**: Android ROM packaging / device product overlay
**Performance Goals**: N/A (functional compatibility)
**Constraints**: No proprietary APKs in Git; no GOLDEN flash; no Magisk/KSU in
`004`; Phonesky signing keys host-only
**Scale/Scope**: Three system packages + permission XMLs + product makefile +
runbook; one rebuild + LAB flash
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
| Gate | Status |
|------|--------|
| LAB only; never GOLDEN | pass |
| No proprietary Google APKs in Git | pass (host `/srv/rom/prebuilts`) |
| Phonesky = patched priv-app, not FakeStore primary | pass |
| No Magisk/KSU in Compatibility path before `006`/`007` | pass (`004` rootless) |
| Spoofing restricted / auditable → full policy in `005` | pass (use LOS baseline only here) |
| Prefer AXP reference for packaging | pass (archive Phonesky + XMLs) |
| Prefer small attributable changes | pass (vendor/product overlay, not megapatch) |
**Post-design**: unchanged — design stays packaging + docs.
## Project Structure
### Documentation (this feature)
```text
specs/004-microg-phonesky-integration/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── packages.md
└── tasks.md
```
### Host + tree (not all in Git)
```text
# Meta repo (this workspace)
docs/004-phonesky-runbook.md
docs/004-lab-selfcheck.md
vendor packaging stubs / product mk (synced to rom-dev01 tree)
# rom-dev01 (canonical build)
/srv/rom/src/lineage/ # LOS tree
/srv/rom/prebuilts/microg/ # GmsCore + GsfProxy APKs (gitignored host)
/srv/rom/prebuilts/phonesky/ # Phonesky APK + permission XMLs
/srv/rom/secrets/phonesky-signing/ # keys if we re-sign (optional first pass)
/srv/rom/artifacts/004-…/ # build zip + self-check evidence
```
## Complexity Tracking
| Violation | Why needed | Simpler alternative rejected because |
|-----------|------------|--------------------------------------|
| None | — | — |
## Phase 0 / Phase 1
See `research.md`, `data-model.md`, `contracts/packages.md`, `quickstart.md`.
@@ -0,0 +1,42 @@
# Quickstart: validate 004 on LAB
## Prerequisites
- `003` vanilla known-good on LAB (or re-flash `004` build cleanly)
- Serial **only** `11231JEC207477`
- Host prebuilts populated per `docs/004-phonesky-runbook.md`
## Build (rom-dev01)
```bash
ssh a16-build # or android-build via a16-build alias
# ensure prebuilts exist
cat /srv/rom/prebuilts/microg/VERSIONS.txt
cat /srv/rom/prebuilts/phonesky/VERSIONS.txt
cd /srv/rom/src/lineage
source build/envsetup.sh
breakfast sunfish userdebug
mka bacon -j8
```
Copy zip to `/srv/rom/artifacts/004-…/` + SHA-256; rsync to laptop.
## Flash LAB
Same flow as `docs/003-flash-lab.md` (dtbo → boot recovery → format if needed →
sideload). **No** GApps add-ons beyond what is already in the ROM image.
## Validate
1. `adb devices` shows LAB only.
2. Package contract (`contracts/packages.md`).
3. Open **microG Settings → Self-Check**; screenshot to
`/srv/rom/artifacts/004-…/selfcheck/` (or laptop `.local-artifacts`).
4. Open Play Store; note whether login/install works or is blocked pending
`005`.
5. Confirm no Magisk/KSU: `pm list packages | grep -iE 'magisk|kernelsu'`.
## Pass criteria
- SC-001…SC-005 from `spec.md`
- Spoofing-only Self-Check failures explicitly listed as `005` follow-ups
@@ -0,0 +1,95 @@
# Research: 004 microG + Phonesky
## Decision: Packaging layout — follow AXP GOLDEN scheme (owner 2026-09-15)
**Decision**: Mirror AXP GOLDEN partition roles. Do not invent a `product`-centric
layout. Measured on GOLDEN (`002`):
| Package | AXP GOLDEN path | Our packaging |
|---------|-----------------|---------------|
| Phonesky | `/system/priv-app/Phonesky_AXP-OS` | **system** + privileged |
| GmsCore | `/system_ext/app/MicrogGmsCore_GH_presigned` | **system_ext** app (not priv-app) |
| GsfProxy | `/system_ext/app/GsfProxy_GH` | **system_ext** app |
Ship via a small `vendor/sunfish-a16` include (partner_gms *pattern*) but
**replace FakeStore** with patched Phonesky. Permission XMLs follow the same
partitions (`system` for Phonesky, `system_ext` for microG).
**Rationale**: Owner — AXP methods exist for a reason; goal is modernize (LOS
23.2 / A16) and later replace Magisk with KSU-Next+SusFS (`007`), not redesign
the compatibility stack. Ad-hoc `LOCAL_PRODUCT_MODULE` + ABI-strip/re-sign of
GmsCore risks different update/SELinux/behaviour vs GOLDEN.
**Alternatives rejected**:
- All on `PRODUCT` — layout drift; hit partition checks awkwardly
- ABI-strip + project re-sign of GmsCore — breaks official microG F-Droid signer
- Stock lineageos4microg + FakeStore — wrong Store model
- Magisk module installers — not for `004` (root is `007`, non-Magisk)
**Partition headroom**: AXP release Phonesky APKs stay **fat** (all ABIs); CI
does not strip x86. If LOS 23.2 sunfish `BOARD_SUPER_PARTITION_ERROR_LIMIT` is
too tight for this package set, raise the error limit modestly (still under
`BOARD_GOOGLE_DYNAMIC_PARTITIONS_SIZE`) rather than breaking microG’s signer.
## Decision: Phonesky binary — patch latest, AXP procedure, project sign
**Decision**: Latest official Play (apkmd) → AXP `patch-playstore` → project JKS
under `/srv/rom/secrets/phonesky-signing/`. XMLs from AXP `etc/`. Do **not** ship
`Phonesky_AXP-OS-signed.apk` as product (wrong trust domain). Keep fat APK unless
a later measured need forces arch filtering (document then).
## Decision: microG — latest official PRESIGNED + F-Droid
**Decision**: Latest official GmsCore **default** APK (`com.google.android.gms-*.apk`
without `-hw`) + GsfProxy, `PRESIGNED`, system_ext like AXP `*_GH_presigned`.
Never re-sign. F-Droid microG repo is the update path. No FakeStore/Companion APK
from microG releases.
**Do not ship `*-hw.apk`**: that Huawei flavor omits
`NetworkLocationService` / `NetworkLocationProviderService` from the manifest, so
microG Location hides Wi‑Fi/cell NLP toggles. GOLDEN ships the default flavor
(`versionName` without `-hw`, e.g. `0.3.15.250932`); LAB previously had
`0.3.16.252432-hw` and only Timeline — corrected 2026-09-16 to
`0.3.16.252432` default for GOLDEN Location UI parity.
## Decision: Signature spoofing in `004`
**Decision**: Use LOS 23.2 restricted spoofing only; gaps → `005`.
## Decision: AXP scheme vs Android 16 / LOS 23.2 (owner 2026-09-15)
**Decision**: Prefer the AXP **A15 / GOLDEN** packaging and behaviour scheme when
LOS 23.2 / Android 16 still supports it. If A16 makes an equivalent path
*easier* (cleaner APIs, existing LOS hooks), use that — still targeting the
same end state (priv Play, updatable official-signer microG, banking path).
If A16 makes the AXP path *harder* or impossible, do **not** silently invent a
divergent stack: document the blocker and redesign the route to the **same**
goals (layout roles, update model, no Magisk-shaped packaging).
**Rationale**: AXP’s choices were intentional; A16 is a port/modernization, not
a license to ignore the oracle. Divergence only when measured necessity.
**Oracle scope (owner 2026-09-16)**: GOLDEN and abu are **Android 15** reference
images — use them for packaging/layout/behaviour (e.g. which GmsCore *flavor*
exposes Location NLP), not as a pin to their older package versions. On LOS 23.2
/ A16 we ship the **newest** fitting official microG (and peers) that preserve
that scheme (default/non-hw GmsCore, not `*-hw`).
**Measured A16 blocker (2026-09-14)**: Putting Phonesky on `system/priv-app`
fails `artifact_path_requirements` (`generic_system.mk`) under LOS 23.2.
**Route to same end state**: keep AXP paths and add them to
`PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST` (same mechanism sunfish already
uses for a few system libs) — do not move Phonesky to `product` just to silence
the check. GmsCore/Gsf on `system_ext` already passed.
**Align-first (owner 2026-09-15)**: Do not invent `privapp-permissions` for
GmsCore while GOLDEN ships it as non-privileged `system_ext` app. Ship only
AXP Phonesky XMLs; repair from Self-Check / build errors if A16 truly requires
more.
## Decision: Signing strategy (FR-010) — locked
| Package | Signer | Update path |
|---------|--------|-------------|
| GmsCore / GsfProxy | Official microG (`PRESIGNED`) | F-Droid microG repo |
| Phonesky | Project JKS `/srv/rom/secrets/phonesky-signing/` | Same-signer patched APKs |
@@ -0,0 +1,178 @@
# Feature Specification: microG + GSF Proxy + patched Phonesky
**Feature Branch**: `004-microg-phonesky-integration`
**Created**: 2026-09-14
**Status**: Draft
**Input**: Continue after `003` gate — integrate Google compatibility stack on LAB
vanilla LOS 23.2 / Android 16 (sunfish), without Magisk/KernelSU yet.
## User Scenarios & Testing *(mandatory)*
### User Story 1 - microG Self-Check green on LAB (Priority: P1)
As the developer, after flashing a build that includes microG + GSF Proxy, I can
open microG Settings Self-Check and see the checks required for basic Google
compatibility pass (or document exact remaining gaps owned by `005`).
**Why this priority**: Without a working microG core, Play and banking paths
cannot start.
**Independent Test**: Flash LAB-only build; open microG Self-Check; capture
screenshots/log; no Magisk/KSU installed.
**Acceptance Scenarios**:
1. **Given** LAB on `003` vanilla baseline, **When** a `004` build is flashed,
**Then** `com.google.android.gms` and `com.google.android.gsf` are present as
system packages and microG Settings opens.
2. **Given** microG is configured (device registration as required), **When**
Self-Check runs, **Then** either all P1 checks pass, or each failure is
explicitly attributed to signature spoofing (`005`) vs packaging error.
---
### User Story 2 - Patched Phonesky as priv-app (Priority: P1)
As the developer, patched Play Store (`com.android.vending`) is installed as a
privileged system app with required permission XMLs, obtained/patched/signed via
a documented host-side procedure (APK never committed to Git).
**Why this priority**: Constitution requires AXP-style patched Phonesky, not
FakeStore→Play as primary.
**Independent Test**: Package present as priv-app; permission XMLs deployed;
Play UI launches; update path documented even if spoofing still incomplete.
**Acceptance Scenarios**:
1. **Given** Phonesky artifacts under a restricted host path, **When** the ROM
is built, **Then** `com.android.vending` is a privileged system app with the
required default-permission / privapp-permissions configuration.
2. **Given** the build is flashed to LAB, **When** Play Store is opened,
**Then** it starts (login/install may still depend on `005` spoofing — mark
clearly if so).
3. **Given** Git history, **When** inspected, **Then** no Google proprietary
Phonesky APK bytes are committed.
---
### User Story 3 - No FakeStore conflict (Priority: P2)
As the developer, FakeStore / microG Companion are not installed when they would
conflict with real patched `com.android.vending`.
**Why this priority**: Prevents the wrong Play stub from masking Phonesky.
**Independent Test**: `pm list packages` shows no conflicting FakeStore when
Phonesky is present.
**Acceptance Scenarios**:
1. **Given** Phonesky is included, **When** packages are listed, **Then**
FakeStore is absent (or explicitly disabled with documented reason).
---
### User Story 4 - Reproducible obtain → patch → sign pipeline (Priority: P2)
As the developer, I can re-run a documented procedure on `rom-dev01` to produce
signed Phonesky + microG packaging inputs without tribal knowledge.
**Why this priority**: Required before any updatable Play Store distribution.
**Independent Test**: Second operator (or clean host path) follows the runbook
and produces matching package names/permissions (signature may differ if keys
differ — keys stay host-only).
**Acceptance Scenarios**:
1. **Given** the `004` runbook, **When** followed on `rom-dev01`, **Then**
inputs land under `/srv/rom/…` (restricted) and the build consumes them
without manual one-off hacks.
2. **Given** signing keys, **When** stored, **Then** they live only under
host secrets (e.g. `/srv/rom/secrets/`), never in Git.
---
### Edge Cases
- LOS 23.2 already ships partial microG spoofing → use it; do not invent a full
spoofing stack in `004` (that is `005`).
- Self-Check fails only on signature-related items → record as `005` dependency,
not a `004` packaging failure.
- Play login/install fails while Self-Check packaging is OK → same split.
- Flash/upgrade wipes user data → LAB only; never GOLDEN.
- Wrong device serial → abort flash.
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: Integrate **latest** official microG Services Core as
`com.google.android.gms` on the LOS 23.2 sunfish LAB build (do not pin to
GOLDEN’s older version).
- **FR-002**: Integrate GSF Proxy as `com.google.android.gsf` (latest upstream).
- **FR-003**: Integrate patched Phonesky as privileged `com.android.vending`
with required privapp-permissions / default-permissions XML, produced by
patching the **latest** official Play Store APK (not an AXP archive binary).
- **FR-004**: Document and implement a reproducible obtain → patch → sign →
priv-app pipeline for Phonesky on `rom-dev01` using AXP `patch-playstore` /
apktool; proprietary APKs MUST NOT be committed to Git.
- **FR-005**: Do not install FakeStore (or conflicting Companion) alongside
patched Phonesky.
- **FR-006**: Record microG Self-Check results on LAB (pass/fail per item) after
flash; attribute spoofing-related failures to `005`.
- **FR-007**: Keep Magisk and KernelSU out of `004` builds (root arrives in
`007`; banking baseline rootless in `006`).
- **FR-008**: Flash and validate on LAB serial `11231JEC207477` only; never
GOLDEN `11071JEC203839`.
- **FR-009**: Prefer AXP golden packaging behaviour as reference (inventory from
`002`); do not blind-copy Magisk modules or ship AXP-signed Play as product.
- **FR-010**: Phonesky is signed with a **project-owned** stable keystore under
`/srv/rom/secrets/phonesky-signing/` so later patched Builds can update Play.
- **FR-011**: microG GmsCore/GsfProxy MUST remain **PRESIGNED** with the
official microG signer so they stay updatable from the microG F-Droid
repository; document repo setup for LAB Self-Check.
### Key Entities
- **microG Services Core** — `com.google.android.gms`
- **GSF Proxy** — `com.google.android.gsf`
- **Patched Phonesky** — `com.android.vending` (external proprietary input)
- **LAB** — Pixel 4a `11231JEC207477` (Android 16 / LOS 23.2 target)
- **GOLDEN** — AXP Pro Android 15 reference (read-only)
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: LAB build includes microG + GSF + patched Phonesky packages with
correct package names.
- **SC-002**: microG Self-Check evidence captured; packaging failures = 0; any
remaining failures mapped to `005` or explicitly accepted.
- **SC-003**: Phonesky opens on LAB; Git contains no proprietary Play APK.
- **SC-004**: Runbook exists such that a clean `rom-dev01` path can rebuild the
same packaging layout.
- **SC-005**: No Magisk/KSU in the `004` validation build.
## Assumptions
- `003` vanilla baseline remains the parent tree; `004` adds packaging/patches
on top without unrelated hardening.
- LineageOS 23.2 microG-oriented spoofing hooks may already exist; full
restricted spoofing policy is `005`.
- `002` / GOLDEN inventory supplies reference behaviour and permission
patterns only — not version pins for microG or Play.
- Phonesky binary is obtained outside Git via latest Play APK + AXP
`patch-playstore` (apktool) + project signing keys on `rom-dev01`.
- Official microG release APKs share the signer used by the microG F-Droid
repo, enabling same-package updates without re-signing.
## Out of Scope
- Signature spoofing implementation beyond using what LOS already provides
(`005`)
- Insular / banking hard-gates (`006`)
- KernelSU (`007`)
- Release signing / AVB relock (`011`)
- Flashing or modifying GOLDEN
@@ -0,0 +1,94 @@
# Tasks: microG + GSF Proxy + patched Phonesky
**Input**: Design docs from `/specs/004-microg-phonesky-integration/`
**Prerequisites**: `003` gate (`docs/003-gate-handoff.md`)
## Phase 1: Setup
- [x] T001 Create host dirs `/srv/rom/prebuilts/{microg,phonesky}/` and
`/srv/rom/artifacts/004-microg/` on `rom-dev01` (android-build writable)
- [x] T002 [P] Add `docs/004-phonesky-runbook.md` (obtain → place APKs → versions
file → no Git) in meta repo
- [x] T003 [P] Add `docs/004-lab-selfcheck.md` stub (Self-Check table + `005`
attribution) in meta repo
## Phase 2: Foundational
- [x] T004 Stage **latest** microG GmsCore + GsfProxy APKs into
`/srv/rom/prebuilts/microg/` + `VERSIONS.txt` (URLs/hashes;
`PRESIGNED` for F-Droid updates) — currently `v0.3.16.252432` /
GsfProxy `v0.1.0` (GitHub latest as of 2026-09-14)
- [x] T005 Obtain **latest** official Play Store (apkmd), patch with AXP
`patch-playstore` + apktool, sign with project JKS under
`/srv/rom/secrets/phonesky-signing/`; stage APK + AXP permission XMLs
into `/srv/rom/prebuilts/phonesky/` + `VERSIONS.txt`
*(Play **52.9.22-24**, not AXP archive)*
- [x] T006 Add LOS product/vendor makefile include that packages the three APKs
as system/priv-app **without FakeStore** (`vendor/sunfish-a16/` on host tree)
- [x] T007 Wire include into `lineage_sunfish` product so `mka bacon` picks it up
## Phase 3: User Story 1 — microG Self-Check (P1)
**Goal**: GmsCore + GsfProxy on LAB; Self-Check evidence
**Independent test**: packages present; Self-Check screenshots
- [x] T008 [US1] Build `004` image (`mka bacon -j8`); log under
`/srv/rom/logs/004-build/` — **superseded by `005` T020 bacon**
(`bacon-20260915T103756Z.log`)
- [x] T009 [US1] Flash LAB only (`11231JEC207477`) per quickstart —
**superseded by `005` T022**
- [x] T010 [US1] Verify `pm path` for `com.google.android.gms` and
`com.google.android.gsf`; fill `docs/004-lab-selfcheck.md` —
**done via `005` US6** (2026-09-15)
## Phase 4: User Story 2 — Patched Phonesky (P1)
**Goal**: priv-app Phonesky with XMLs; Play launches
**Independent test**: `pm path com.android.vending`; app opens
- [x] T011 [US2] Confirm privapp-permissions XMLs installed on device
(`/etc/permissions` or equivalent) — **covered by `005` packaging**
- [x] T012 [US2] Launch Play Store on LAB; record result — **pass after
`005` spoof + exact-alarm** (was blocked → `005`, now closed)
- [x] T013 [US2] Confirm Git/meta repo contains **no** Phonesky APK bytes
## Phase 5: User Story 3 — No FakeStore (P2)
- [x] T014 [US3] `pm list packages` shows no FakeStore; document in self-check
— **`docs/004-lab-selfcheck.md` 2026-09-15**
## Phase 6: User Story 4 — Reproducible pipeline (P2)
- [x] T015 [US4] Finalize runbook with exact copy commands from AXP archive +
microG download pins — **`docs/004-phonesky-runbook.md` + `005` runbook**
- [x] T016 [US4] Document FR-010 signing strategy (project JKS under
`/srv/rom/secrets/phonesky-signing/`; GmsCore stays PRESIGNED)
## Phase 7: Polish
- [x] T017 Collect artifacts: zip SHA-256, `VERSIONS.txt` copies, Self-Check
evidence → **`005` artifacts `20260915-bacon` + `20260915-us6-validation`**
- [x] T018 Update `docs/axp-pro-parity.md` rows microG/Phonesky status —
**done in `005` T027**
- [x] T019 Handoff note: spoofing done in `005`; next `006` banking rootless;
no Magisk/KSU in this build
## Dependencies
- T001–T003 before staging
- T004–T007 before build
- T008–T010 (US1) before claiming microG done
- T011–T013 (US2) can overlap validation with US1 after same flash
- T014 after flash
- T015–T016 can refine docs in parallel with builds
- T017–T019 after validation
## MVP
T001–T012: packages on LAB + Self-Check notes + Play launch attempt.
## Parallel opportunities
- T002/T003 with T001
- T004/T005 in parallel
- Doc polish T015/T016 while T008 builds
@@ -0,0 +1,39 @@
# Specification Quality Checklist: Restricted native signature spoofing
**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-09-15
**Feature**: [spec.md](../spec.md)
## Content Quality
- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed
## Requirement Completeness
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified
## Feature Readiness
- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification
## Notes
- Oracle path and package names appear as acceptance identifiers (required for
LAB/GOLDEN verification); patch file paths belong in `/speckit-plan`, not here.
- Checklist validated 2026-09-15 after initial draft — ready for `/speckit-plan`.
- Spec updated same day with **First working stack** (US6, FR-010/011, SC-007/008):
folds `004` packaging leftovers + BeaconDB location into this phase; keeps
`006`/`007` out. Re-validated for plan. Plan artifacts generated 2026-09-15.
@@ -0,0 +1,59 @@
# Contract: Spoofing surface (LAB / user-visible)
## Settings
| Interface | Contract |
|-----------|----------|
| Secure setting | `allow_signature_fake` ∈ {`null`/`0`, `1`} |
| Readback | `adb shell settings get secure allow_signature_fake` |
| UI | Developer Options exposes enable/disable; OFF disables effect of grants |
## Permission
| Interface | Contract |
|-----------|----------|
| Permission name | `android.permission.FAKE_PACKAGE_SIGNATURE` |
| `pm list permissions` | Permission is listed (not “Unknown permission”) |
| Grant | Visible in `dumpsys package <pkg>` for authorized packages when user granted |
## PackageManager behaviour
When **toggle ON** and package **has grant** and **has** `meta-data fake-signature`
(or microG’s equivalent spoof path):
- Callers using legacy signature APIs and signing-certificate APIs MUST observe
the faked Google identity for that package (sufficient for microG Self-Check
“correct signature” and Play OAuth registration checks).
When **toggle OFF**:
- Effective spoofing MUST NOT occur (silent deny).
## Allow-list
| Package | Required |
|---------|----------|
| `com.google.android.gms` | yes |
| `com.android.vending` | yes |
| Other packages | denied unless explicitly added later under project policy |
## First-stack package registration
| Package | `pm path` expectation |
|---------|----------------------|
| `com.google.android.gms` | system_ext app path |
| `com.google.android.gsf` | system_ext app path |
| `com.android.vending` | system priv-app Phonesky path |
PRESIGNED APKs in the shipped image MUST verify with APK Signature Scheme v2/v3
(no post-sign strip).
## Location (documentation contract)
Quickstart MUST specify:
1. Open microG → Location
2. Enable network/Wi-Fi location as required by UI
3. Set service URL to BeaconDB (or documented successor), not MLS
No requirement to install UnifiedNlp backend packages.
@@ -0,0 +1,70 @@
# Data model: 005 spoofing + first-stack entities
## GlobalSpoofingToggle
| Field | Description |
|-------|-------------|
| Setting key | `settings secure allow_signature_fake` (AXP/GOLDEN) |
| Values | `0` / unset = OFF; `1` = ON |
| Default | OFF |
| UI | Developer Options → Signature spoofing support → Allow signature spoofing |
| Effect when OFF | Prior per-app grants ignored (silent deny) |
## SpoofingPermission
| Field | Description |
|-------|-------------|
| Name | `android.permission.FAKE_PACKAGE_SIGNATURE` |
| Protection | dangerous (user-grantable), as on GOLDEN |
| Grant state | Per package / per user; GOLDEN uses `USER_SET` for gms + vending |
| Revoke UI | App permissions → Spoof package signature (AXP strings) |
## AuthorizedPackage
| Package | Signer identity | Notes |
|---------|-----------------|-------|
| `com.google.android.gms` | Official microG PRESIGNED | System_ext app; F-Droid updates |
| `com.android.vending` | Project Phonesky JKS | System priv-app; `fake-signature` meta-data |
Extension of this set requires constitution-aligned review (not open allow-all).
## FakeSignatureMetadata
| Field | Description |
|-------|-------------|
| APK meta-data name | `fake-signature` |
| Value | Hex-encoded Google platform certificate (injected by AXP `patch-playstore`) |
| Consumer | PackageManager spoofing path when permission granted + toggle ON |
## PackagingArtifacts (first-stack ship)
| Artifact | Rule |
|----------|------|
| GmsCore / Gsf / Phonesky APKs | PRESIGNED install without zip-realign strip (`REPLACE_PREBUILT`) |
| Phonesky privapp XML | Includes FAKE_PACKAGE + A16 FGS / exact-alarm / Play 52 extras; digest = **our** cert |
| Default-permissions / sysconfig | Align with GOLDEN `er_*` intent (power-save, app-link) as needed |
## LocationConfig (validation, not a package)
| Field | Description |
|-------|-------------|
| Provider | microG built-in network location (0.3.x) |
| Endpoint | Ichnaea-compatible URL (BeaconDB: `https://api.beacondb.net`) |
| Non-goals | UnifiedNlp backend APKs; Mozilla MLS |
## State transitions
```text
[Toggle OFF] --enable in Dev Options--> [Toggle ON]
| |
v v
spoof denied prompts/grants for
for all apps authorized packages
|
v
[Permission granted]
|
v
PackageInfo signatures + signingInfo
present Google identity to callers
```
@@ -0,0 +1,98 @@
# Implementation Plan: Restricted native signature spoofing
**Branch**: `005-restricted-signature-spoofing` | **Date**: 2026-09-15 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `/specs/005-restricted-signature-spoofing/spec.md`
## Summary
Deliver the **first working Google-compat stack on LAB**: finish `004` packaging
gates (PRESIGNED replace + Play FGS privapp), add **AXP/EXTENDROM-style
restricted signature spoofing** (Developer Options `allow_signature_fake` +
`FAKE_PACKAGE_SIGNATURE` + allow-list for microG + Phonesky), validate Play auth
/ Self-Check, and document **modern** microG location (BeaconDB). Do **not**
pull `006` banking or `007` root into this phase.
Primary technical approach: attribute patches from
[EXTENDROM_SIGNATURE_SPOOFING](https://github.com/sfX-android/android_vendor_extendrom/wiki/EXTENDROM_SIGNATURE_SPOOFING)
(A15 proven on GOLDEN) and port to **Android 16 / LOS 23.2**, ensuring both
legacy `PackageInfo.signatures` and modern `signingInfo` are spoofed.
## Technical Context
**Language/Version**: Android frameworks Java/Kotlin patches; Settings overlays;
`Android.mk` / product makefiles; bash runbooks on `rom-dev01`
**Primary Dependencies**: LOS 23.2 tree; EXTENDROM/AXP sigspoof patches (A15 →
A16 port); microG 0.3.x; project-signed Phonesky; GOLDEN 2026-09-15 capture
**Storage**: Host `/srv/rom/` prebuilts + secrets; meta-repo `vendor/sunfish-a16/`
**Testing**: LAB flash only; `settings`/`pm`/`dumpsys`; microG Self-Check;
Play login logcat; optional BeaconDB location smoke
**Target Platform**: Pixel 4a LAB `11231JEC207477`; Android 16 / LOS 23.2
**Project Type**: Android ROM frameworks + Settings + packaging overlay
**Performance Goals**: N/A (correctness / compatibility)
**Constraints**: LAB only; no GOLDEN modify; no Magisk/KSU; restricted allow-list;
attributable patches; no proprietary APKs in Git
**Scale/Scope**: frameworks/base (+ services) spoofing; Settings Developer
Options; privapp/default/sysconfig XML updates; one bacon + LAB flash; docs
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
| Gate | Status |
|------|--------|
| LAB only; never GOLDEN | pass |
| Spoofing native, restricted, auditable; no LSPosed | pass (FR-003/006) |
| Prefer package/signer allow-list over open fake-signature | pass |
| Works without optional root | pass (FR-006, US5) |
| Prefer AXP/EXTENDROM behaviour; A16 port if needed | pass |
| No proprietary APKs in Git | pass |
| Small attributable changes | pass (patch series from EXTENDROM/AXP) |
| Do not invent `006`/`007` into this feature | pass (spec Out of Scope) |
**Post-design**: unchanged — design stays spoofing + first-stack ship gates.
## Project Structure
### Documentation (this feature)
```text
specs/005-restricted-signature-spoofing/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── spoofing-surface.md
└── tasks.md # via /speckit-tasks (not this command)
```
### Host + tree
```text
# Meta repo
vendor/sunfish-a16/ # packaging (REPLACE, privapp XML, gms.mk)
docs/a15-a16-delta.md
docs/004-*.md # packaging/selfcheck (update for BeaconDB)
.local-artifacts/002-golden/20260915-spoofing-phonesky/ # oracle
# rom-dev01
/srv/rom/src/lineage/frameworks/base/ # spoofing patches
/srv/rom/src/lineage/packages/apps/Settings/ # Developer Options toggle
/srv/rom/references/… or vendor/extendrom/ # patch source (checkout as needed)
/srv/rom/artifacts/005-…/ # validation zip + evidence
```
**Structure Decision**: Continue `vendor/sunfish-a16` packaging overlay; apply
spoofing as attributable patch series under the LOS tree on `rom-dev01` (not a
megapatch in Git). Meta-repo holds vendor stubs, docs, and specs only.
## Complexity Tracking
| Violation | Why needed | Simpler alternative rejected because |
|-----------|------------|--------------------------------------|
| Frameworks patches | LOS 23.2 has no FAKE_PACKAGE | Magisk/LSPosed forbidden by constitution |
## Phase 0 / Phase 1
See `research.md`, `data-model.md`, `contracts/spoofing-surface.md`, `quickstart.md`.
@@ -0,0 +1,74 @@
# Quickstart: 005 first working stack (LAB only)
**Device**: LAB `11231JEC207477` — never GOLDEN `11071JEC203839`
## Goal
One flash proves: microG + patched Play + restricted spoofing + modern location
docs. Not banking (`006`), not root (`007`).
## Prerequisites
- Artifact zip from `005` bacon (includes `REPLACE_PREBUILT` + updated privapp)
- Laptop `adb`/`fastboot`; hashes verified
- Oracle notes (optional): `.local-artifacts/002-golden/20260915-spoofing-phonesky/README.md`
## Flash
Follow `docs/003-flash-lab.md` pattern with the **005** zip (dtbo → boot/recovery
→ format if needed → sideload). Say **No** to additional packages.
## After first boot
```bash
SERIAL=11231JEC207477
adb -s "$SERIAL" shell getprop ro.lineage.version
adb -s "$SERIAL" shell pm path com.google.android.gms
adb -s "$SERIAL" shell pm path com.google.android.gsf
adb -s "$SERIAL" shell pm path com.android.vending
# expect all three present — no remount push
```
## Spoofing
1. Enable **Developer options**
2. Enable **Allow signature spoofing** (`settings get secure allow_signature_fake` → `1`)
3. Open **microG Settings → Self-Check**
4. Grant spoofing for GmsCore and Play when prompted (or App info → permissions)
5. Confirm Self-Check **correct signature** for GmsCore and Play/Companion
```bash
adb -s "$SERIAL" shell settings get secure allow_signature_fake
adb -s "$SERIAL" shell dumpsys package com.google.android.gms | grep FAKE_PACKAGE_SIGNATURE
adb -s "$SERIAL" shell dumpsys package com.android.vending | grep FAKE_PACKAGE_SIGNATURE
```
## Play auth
1. Open Play Store; sign in
2. Confirm UI stays up (no FGS crash loop)
3. Optional logcat gate: **no** `UNREGISTERED_ON_API_CONSOLE` for Phonesky
In-app Play self-update may still fail (AXP-like) — acceptable.
## Modern location
1. microG Settings → Location
2. Enable network location as shown
3. Configure service URL: `https://api.beacondb.net` (BeaconDB; MLS is dead)
4. Do **not** install Déjà Vu / Nominatim NLP backend APKs
## Pass criteria (SC)
- [x] Toggle + `allow_signature_fake=1` *(LAB 2026-09-15)*
- [x] Self-Check correct signature for gms + vending
- [x] Play sign-in without API-console fingerprint error
- [x] All three packages from zip without remount *(gms/gsf system; vending upgraded in-place for exact-alarm — next bacon restores system path)*
- [x] BeaconDB steps recorded; no UnifiedNlp backends required
- [x] No Magisk/KSU on the build
## Failure
Capture logcat (`vending`, `GmsAuthenticator`, `SelfCheck`, `PackageManager`).
Do not flash GOLDEN. Packaging-only regressions → fix under vendor overlay;
spoofing regressions → frameworks/Settings patches.
@@ -0,0 +1,104 @@
# Research: 005 restricted signature spoofing
## Decision: First working stack = `004` leftovers + spoofing + modern location
**Decision**: Ship one LAB validation build that includes (1) unfinished `004`
packaging fixes, (2) restricted spoofing, (3) BeaconDB location docs — and
**exclude** `006` banking and `007` root.
**Rationale**: Owner: first phase that must work is microG + patched Play +
spoofing. Banking/root are later regressions.
**Alternatives considered**: Separate tiny `004b` rebuild only — rejected;
wastes a flash cycle before spoofing lands. Pulling `006` early — rejected;
blocks focus.
## Decision: Spoofing implementation source = EXTENDROM / AXP (port to A16)
**Decision**: Use [EXTENDROM_SIGNATURE_SPOOFING](https://github.com/sfX-android/android_vendor_extendrom/wiki/EXTENDROM_SIGNATURE_SPOOFING)
as the attributable primary source (matches GOLDEN behaviour:
`allow_signature_fake`, Developer Options, per-app `FAKE_PACKAGE_SIGNATURE`).
Port patches from the closest supported Android line (**A15** is STABLE/tested;
GOLDEN is A15) onto **A16 / LOS 23.2**.
**Rationale**: Stock LOS 23.2 tree on `rom-dev01` has **no**
`FAKE_PACKAGE_SIGNATURE` / `allow_signature_fake` (inventory 2026-09-15). AXP
GOLDEN proves the UX and setting key. Old `android_signature_spoofing` repo is
obsolete per upstream.
**Alternatives considered**:
| Option | Why not |
|--------|---------|
| lineageos4microg unrestricted spoof | Constitution forbids open spoofing |
| Magisk / LSPosed FakeGApps | Forbidden; rootless Compatibility MVP |
| Wait for LOS to grow A16 EXTENDROM dir | Blocks first stack; we port ourselves |
**A16 note**: EXTENDROM wiki table lists A15, not A16. Expect conflict resolution
(`repo diff` / split patches) per EXTENDROM porting guide. Record deltas in
`docs/a15-a16-delta.md`.
## Decision: Must spoof `signingInfo`, not only `signatures[]`
**Decision**: Implementation MUST spoof both legacy `PackageInfo.signatures` and
modern `PackageInfo.signingInfo` (GET_SIGNING_CERTIFICATES).
**Rationale**: microG #2680 / EXTENDROM #48 — Google/Play check `signingInfo` on
modern Android; Self-Check “correct signature” fails if only legacy arrays are
faked. LOS Gerrit has A13–A15 fixes; ensure A16 port includes equivalent.
## Decision: Allow-list = microG GmsCore + project Phonesky
**Decision**: Effective spoofing limited to:
- `com.google.android.gms` — official microG signer (`73aaa390…` short on GOLDEN)
- `com.android.vending` — project Phonesky JKS
SHA-256 digest
`31:31:86:CB:7D:F2:D4:01:33:72:12:07:B7:29:73:E8:52:F6:1C:87:67:C3:21:E5:C7:50:39:01:1B:83:BD:56`
Privapp/default XMLs that bind by cert digest MUST use **our** digest (not
AXP’s `00:5C:98:05:…`).
**Rationale**: Constitution + GOLDEN grants; AXP `er_*` XMLs bind Phonesky by
digest.
## Decision: Global toggle default OFF
**Decision**: `allow_signature_fake` defaults to disabled; user enables in
Developer Options (AXP setup guide).
**Rationale**: Matches oracle docs and reduces surprise attack surface.
## Decision: `004` packaging leftovers are a `005` ship gate
**Decision**: Before declaring `005` done, the flashable zip MUST include:
1. `LOCAL_REPLACE_PREBUILT_APK_INSTALLED` for GmsCore/Gsf/Phonesky (already in
working-tree `vendor/sunfish-a16/Android.mk`)
2. Phonesky privapp extras for Play 52 / A16 FGS:
`FOREGROUND_SERVICE_SYSTEM_EXEMPTED`, `SCHEDULE_EXACT_ALARM` /
`USE_EXACT_ALARM`, plus prior grammatical/status-bar entries
**Rationale**: Measured LAB failures — PRESIGNED strip → no `pm path`; missing
FGS allowlist → VpaService/DownloadService crash. Remount pushes are not the
shipped artifact.
## Decision: Modern location = BeaconDB (not UnifiedNlp backends)
**Decision**: Document configuring microG Location to use an Ichnaea-compatible
URL (`https://api.beacondb.net` or documented successor). Do **not** package
Déjà Vu / Nominatim NLP / MLS apps.
**Rationale**: microG 0.3.x dropped UnifiedNlp backends; MLS retired (2025);
GOLDEN also has no NLP backend packages. Owner wants modern stack.
**Alternatives considered**: Ship UnifiedNlp backends — rejected (obsolete for
0.3.x primary path).
## Decision: Out of scope remains `006`/`007`
**Decision**: No Insular/banking hard-gates; no KSU/SusFS in this feature’s
acceptance.
**Rationale**: Owner confirmation 2026-09-15 — first phase is microG + Play +
spoofing only.
@@ -0,0 +1,277 @@
# Feature Specification: Restricted native signature spoofing
**Feature Branch**: `005-restricted-signature-spoofing`
**Created**: 2026-09-15
**Status**: Draft
**Input**: Advance spoofing after `004` packaging: LAB Play/microG Self-Check and
Google auth fail without AXP-style restricted signature spoofing. GOLDEN capture
2026-09-15 is the behavioural oracle. Prefer modern LOS 23.2 / A16 path to the
**same end state** as AXP (not Magisk modules).
## First working stack (this phase — before `006`/`007`)
This feature closes the **first usable Google-compat stack on LAB**:
| Must work in this phase | Owner |
|-------------------------|--------|
| Official microG + GSF (PRESIGNED, F-Droid-updatable) | `004` packaging + `005` ship |
| Patched Phonesky priv-app (project-signed, disable/enable OK) | `004` + `005` ship |
| Restricted spoofing (Developer Options + grants) | **`005` core** |
| Self-Check correct signature + Play auth (no API-console mismatch) | **`005`** |
| Modern network location (BeaconDB / Ichnaea-compatible URL; not Déjà Vu NLP) | `005` validation / docs |
| `004` packaging leftovers that block a clean flashable zip | **`005` ship gate** (`REPLACE_PREBUILT`, Play FGS privapp) |
**Explicitly later (do not pull forward):**
- `006` — Insular / Yuh / Swissquote banking hard-gates
- `007` — KernelSU-Next / SusFS / root hide
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Developer Options master switch (Priority: P1)
As the developer (and later daily-driver user), I can enable **Allow signature
spoofing** under Developer Options. When off, spoofing has no effect even if
per-app grants remain. When on, authorized apps may use spoofing after grant.
**Why this priority**: Matches GOLDEN/AXP (`settings secure allow_signature_fake`)
and constitution (not unrestricted platform fake-signature).
**Independent Test**: On LAB build with `005`, toggle the setting; read
`settings get secure allow_signature_fake` (0/1); confirm UI strings exist.
**Acceptance Scenarios**:
1. **Given** a `005` build on LAB, **When** Developer Options is opened,
**Then** a **Signature spoofing support** category exposes **Allow signature
spoofing** (AXP-equivalent UX).
2. **Given** the toggle is OFF, **When** an app holds a prior spoofing grant,
**Then** spoofing has **no effect** (same as AXP documented behaviour).
3. **Given** the toggle is ON, **When** `settings get secure allow_signature_fake`
is read, **Then** the value is `1`.
---
### User Story 2 - microG + Phonesky Self-Check signatures (Priority: P1)
As the developer, with global spoofing ON and spoofing permission granted to
microG GmsCore and patched Phonesky, microG Self-Check marks **correct
signature** for both `com.google.android.gms` and `com.android.vending`
(Companion/Play Store line), not only “installed”.
**Why this priority**: Unblocks the `004` gap that packaging alone cannot fix;
required before Play OAuth works.
**Independent Test**: LAB only; no GOLDEN modification; screenshots/log of
Self-Check before/after grants.
**Acceptance Scenarios**:
1. **Given** `004` packages present and global spoofing ON, **When**
`FAKE_PACKAGE_SIGNATURE` is granted to GmsCore and Phonesky, **Then**
Self-Check shows positive **correct signature** for both.
2. **Given** Self-Check, **When** the user taps the resolution for correct
signature on Play/microG, **Then** a grant path exists (system permission UI
or equivalent) — not a missing activity / unknown permission.
3. **Given** global spoofing OFF, **When** Self-Check runs, **Then** correct
signature checks fail even if packages are installed.
---
### User Story 3 - Play login / auth without API-console mismatch (Priority: P1)
As the developer, after spoofing is enabled and granted for Phonesky, Google
account sign-in in Play no longer fails with
`UNREGISTERED_ON_API_CONSOLE` / “SHA-1 certificate fingerprint” mismatch caused
by presenting the project signing key as the Play identity.
**Why this priority**: Owner-observed LAB failure; AXP GOLDEN works with spoofing.
**Independent Test**: LAB logcat during Play sign-in; compare to pre-`005`
failure mode.
**Acceptance Scenarios**:
1. **Given** spoofing ON + Phonesky granted, **When** signing into Play,
**Then** auth token acquisition does not fail with `UNREGISTERED_ON_API_CONSOLE`
due to project cert fingerprint.
2. **Given** Play update prompts that AXP intentionally blocks, **When** the
user tries in-app self-update, **Then** failure remains acceptable (out of
scope to enable stock Play self-update).
---
### User Story 4 - Restricted allow-list only (Priority: P1)
As the project owner, spoofing is **not** a general capability for arbitrary
apps. Only explicitly authorized packages/signers may obtain effective spoofing
(at minimum official microG GmsCore and project-signed patched Phonesky).
**Why this priority**: Constitution: native, restricted, auditable; no LSPosed.
**Independent Test**: Attempt spoofing from a third-party test APK that requests
`FAKE_PACKAGE_SIGNATURE` without being allow-listed → denied.
**Acceptance Scenarios**:
1. **Given** global spoofing ON, **When** a non-authorized app requests
spoofing, **Then** it cannot effectively impersonate Google/platform
signatures.
2. **Given** documentation, **When** reviewed, **Then** the authorized set and
how to extend it are written (for future packages if needed).
---
### User Story 5 - Survives without root (Priority: P2)
As the developer, microG and Phonesky spoofing keep working when optional root /
Zygisk-style functionality is unavailable (Compatibility MVP is rootless).
**Why this priority**: Constitution Compatibility MVP ends at `006` rootless.
**Independent Test**: LAB build without Magisk/KSU; spoofing + Self-Check still
pass.
**Acceptance Scenarios**:
1. **Given** no Magisk/KSU on the `005` validation build, **When** spoofing is
configured, **Then** Self-Check signature lines and basic Play auth path
still succeed.
---
### User Story 6 - First working stack ship gate (Priority: P1)
As the developer, one LAB flash of the `005` validation build includes a
**complete** first stack: packaging fixes from `004` that never landed in a
clean zip, spoofing, and documented modern location setup — without waiting for
banking (`006`) or root (`007`).
**Why this priority**: Owner: this phase must work before later features.
**Independent Test**: Single zip flash on LAB; follow quickstart; no Magisk/KSU.
**Acceptance Scenarios**:
1. **Given** the `005` artifact zip, **When** flashed to LAB, **Then** Phonesky
registers (`pm path`) without remount hacks; APK signatures verify (no
PRESIGNED strip).
2. **Given** that build, **When** spoofing is configured per quickstart,
**Then** Self-Check signature lines and Play auth meet SC-003/SC-004.
3. **Given** microG Location settings, **When** configured for a modern
Ichnaea-compatible endpoint (BeaconDB), **Then** the procedure is documented
and does not depend on legacy UnifiedNlp backend APKs.
---
### Edge Cases
- Stock LOS 23.2 tree on `rom-dev01` has **no** `FAKE_PACKAGE_SIGNATURE` /
`allow_signature_fake` (inventory 2026-09-15) → must port/adapt from
AXP/EXTENDROM patterns, not “enable a hidden LOS flag”.
- Phonesky privapp XML on AXP binds permissions to **cert SHA-256 digest**; our
project digest differs from AXP’s — XML must use **our** digest.
- Play 52.x FGS / privapp extras (`FOREGROUND_SERVICE_SYSTEM_EXEMPTED`, etc.)
remain packaging/`004` follow-ups; `005` must not regress them.
- Disabling Play via App info remains supported (owner: useful on AXP).
- Island/work profile: spoofing grants may need to exist per user — verify at
least owner user 0 for P1; note multi-user as follow-up if broken.
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: ROM MUST provide a Developer Options control equivalent to AXP
**Allow signature spoofing**, persisted as a global setting (GOLDEN:
`settings secure allow_signature_fake`).
- **FR-002**: ROM MUST define and enforce `android.permission.FAKE_PACKAGE_SIGNATURE`
(or AOSP-equivalent restricted spoofing permission) so PackageManager can
honour `meta-data fake-signature` for authorized packages when the global
toggle is ON and the permission is granted.
- **FR-003**: Spoofing MUST be restricted to an explicit allow-list of packages
and/or signers; at minimum `com.google.android.gms` (official microG signer)
and `com.android.vending` (project Phonesky signer). Unrestricted spoofing is
forbidden.
- **FR-004**: With toggle ON and grants present, microG Self-Check MUST report
positive **correct signature** for GmsCore and Phonesky/Play.
- **FR-005**: With toggle ON and Phonesky granted, Play account auth MUST NOT
fail with `UNREGISTERED_ON_API_CONSOLE` caused by exposing the project signing
certificate as Play’s identity.
- **FR-006**: Spoofing MUST NOT depend on Magisk, Zygisk, LSPosed, or other
root modules.
- **FR-007**: Behaviour and UX SHOULD match GOLDEN/AXP where compatible with
Android 16 / LOS 23.2; if the AXP patch set does not apply cleanly, redesign
the path to the **same end state** and record deltas in `docs/a15-a16-delta.md`.
- **FR-008**: Validation MUST be LAB-only (`11231JEC207477`); GOLDEN remains
sample-only.
- **FR-009**: Implementation MUST be attributable (source-linked patches /
EXTENDROM/AXP/LOS references); no opaque megapatch.
- **FR-010**: The `005` validation build MUST incorporate outstanding `004`
packaging fixes required for a clean flashable image: PRESIGNED APKs installed
via replace-prebuilt (no v2/v3 strip), and Phonesky privapp entries needed for
Android 16 / Play 52 FGS (e.g. system-exempted foreground service + exact
alarm) so Play does not crash on setup/download services.
- **FR-011**: First-stack validation MUST document modern microG network
location configuration (Ichnaea-compatible service URL such as BeaconDB); MUST
NOT require shipping legacy UnifiedNlp backend packages (Déjà Vu, Nominatim
NLP apps).
### Key Entities
- **Global spoofing toggle**: User-facing Developer Options switch; gates all
spoofing effect.
- **Spoofing permission**: Per-app dangerous/runtime-style permission the user
(or approved default path) grants to authorized packages.
- **Authorized package set**: microG GmsCore + patched Phonesky (extensible
later under constitution rules).
- **Oracle capture**: `.local-artifacts/002-golden/20260915-spoofing-phonesky/`
(settings, `er_*` XMLs, framework strings, dumpsys grants).
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: On LAB, Developer Options exposes the spoofing master switch;
`allow_signature_fake` reads `1` when enabled.
- **SC-002**: `pm` / permission APIs show `FAKE_PACKAGE_SIGNATURE` defined;
grants visible for GmsCore and Phonesky when configured.
- **SC-003**: microG Self-Check shows **correct signature** checked for GmsCore
and Play/Companion within one configuration session after flash.
- **SC-004**: Play sign-in logcat no longer shows `UNREGISTERED_ON_API_CONSOLE`
fingerprint mismatch for Phonesky under the spoofed identity.
- **SC-005**: A non-allow-listed test app cannot obtain effective Google
signature spoofing with the global toggle ON.
- **SC-007**: One LAB flash of the `005` zip boots without remount overlays for
Phonesky; `apksigner verify` equivalent on-device package is registered; Play
does not crash-loop on VpaService/DownloadService for missing FGS privapp
allowlist entries.
- **SC-008**: Quickstart includes BeaconDB (or successor) location steps;
Self-Check / location UI does not depend on empty UnifiedNlp backend lists.
## Assumptions
- `004` packaging (PRESIGNED `LOCAL_REPLACE_PREBUILT_APK_INSTALLED`, Phonesky
patch with `fake-signature`, privapp XMLs) remains the base; `005` does not
replace FakeStore for Play. Unmerged `004` leftovers ship in the same `005`
validation build (single first-stack artifact).
- Default for the global toggle is **OFF** until the user enables it (AXP
“explicitly enable” model), unless plan proves a safer first-boot prompt.
- Official microG continues to update via F-Droid same-signer; Phonesky via
project JKS.
- Primary patch reference: AXP/EXTENDROM signature spoofing + GOLDEN 2026-09-15
capture; inventory shows stock LOS 23.2 lacks these hooks. Spoofing MUST also
cover modern `PackageInfo.signingInfo` (not only legacy `signatures[]`).
- Play self-update remaining broken/blocked is acceptable (AXP parity).
- Multi-user/Island spoofing parity is best-effort for P1 (user 0 first).
- MLS (Mozilla Location Service) is retired; BeaconDB (or documented successor)
is the modern network-location endpoint for microG 0.3.x.
## Out of Scope
- KernelSU / Magisk / DenyList (`007`)
- Banking Insular hard-gates / Yuh / Swissquote (`006`)
- Call recording / FLAG_SECURE (`008`/`009`)
- Changing Phonesky to Google’s proprietary signer
- Unrestricted signature spoofing for all apps
- Reintroducing UnifiedNlp backend APKs as the primary location design
@@ -0,0 +1,264 @@
# Tasks: Restricted native signature spoofing (first working stack)
**Input**: Design docs from `/specs/005-restricted-signature-spoofing/`
**Prerequisites**: `004` packaging on LAB (Phonesky present); GOLDEN oracle
`.local-artifacts/002-golden/20260915-spoofing-phonesky/`; constitution spoofing rules
**Organization**: First working stack = packaging leftovers + spoofing + BeaconDB.
**Out of scope**: `006` banking, `007` KSU/root.
## Format: `[ID] [P?] [Story] Description`
- **[P]**: Can run in parallel (different files, no incomplete deps)
- **[Story]**: US1…US6 from `spec.md`
## Path conventions
- Meta repo: `vendor/sunfish-a16/`, `docs/`, `specs/005-…/`
- Build host: `/srv/rom/src/lineage/` (frameworks, Settings), `/srv/rom/artifacts/005-…/`
---
## Phase 1: Setup
**Purpose**: Patch sources, dirs, delta notes
- [x] T001 Checkout / mirror EXTENDROM sigspoof sources on `rom-dev01` under
`/srv/rom/references/extendrom/` (or sparse clone
`sfX-android/android_vendor_extendrom`); record commit in
`docs/a15-a16-delta.md`
*(2026-09-15: `f129253c…`; also found stock LuK1337 spoof in LOS tree)*
- [x] T002 [P] Create `/srv/rom/artifacts/005-spoofing/` and
`/srv/rom/logs/005-build/` (android-build writable)
- [x] T003 [P] Add `docs/005-spoofing-runbook.md` stub (EXTENDROM port steps,
allow-list, LAB-only) in meta repo
- [x] T004 [P] Confirm working-tree packaging leftovers present:
`vendor/sunfish-a16/Android.mk` has `LOCAL_REPLACE_PREBUILT_APK_INSTALLED`;
`vendor/sunfish-a16/permissions/privapp-permissions-com.android.vending.xml`
has FGS/exact-alarm + Play 52 entries — sync both to
`/srv/rom/src/lineage/vendor/sunfish-a16/`
---
## Phase 2: Foundational (blocks all stories)
**Purpose**: A16-portable spoofing patch set + product wiring; no bacon yet
**⚠️ CRITICAL**: No US validation flash until this phase completes
- [x] T005 Inventory EXTENDROM A15 patch list vs LOS 23.2 paths; write mapping
table in `docs/a15-a16-delta.md` (frameworks/base, services, Settings,
overlays)
- [x] T006 Apply / port EXTENDROM signature-spoofing patches to
`/srv/rom/src/lineage/frameworks/base/` so
`android.permission.FAKE_PACKAGE_SIGNATURE` exists and
`meta-data fake-signature` is honoured for **both**
`PackageInfo.signatures` and `PackageInfo.signingInfo`
*(extended LuK1337 path via `apply_005_sigspoof.py`)*
- [x] T007 Wire global gate `settings secure allow_signature_fake` in
PackageManager path under
`/srv/rom/src/lineage/frameworks/base/services/` (silent deny when off)
- [x] T008 Restrict effective spoofing to allow-list packages/signers
(`com.google.android.gms` official microG; `com.android.vending` project
Phonesky digest
`31:31:86:CB:7D:F2:D4:01:33:72:12:07:B7:29:73:E8:52:F6:1C:87:67:C3:21:E5:C7:50:39:01:1B:83:BD:56`)
— document mechanism in `docs/005-spoofing-runbook.md`
- [x] T009 [P] Update Phonesky privapp/default XMLs in
`vendor/sunfish-a16/permissions/` to include `FAKE_PACKAGE_SIGNATURE` and
bind Phonesky entries to **our** cert SHA-256 digest (not AXP
`00:5C:98:…`); sync to host tree
*(FAKE_PACKAGE already in privapp; digest enforced in ComputerEngine)*
- [ ] T010 [P] Align GmsCore-related default-permissions / sysconfig intent from
GOLDEN `er_*` files into `vendor/sunfish-a16/` (power-save / app-link as
needed) without inventing GmsCore priv-app
**Checkpoint**: Foundation ready — bacon is next (US6)
---
## Phase 3: User Story 1 — Developer Options master switch (P1) 🎯 MVP slice
**Goal**: Toggle **Allow signature spoofing** in Developer Options
**Independent test**: UI present; `settings get secure allow_signature_fake` is
`1` when ON; OFF disables effect
- [x] T011 [US1] Port EXTENDROM/AXP Settings Developer Options strings +
preference for `allow_signature_fake` into
`/srv/rom/src/lineage/packages/apps/Settings/` (or overlay under
`vendor/sunfish-a16/`) matching GOLDEN UX
(`signature_spoof_category` / Allow signature spoofing)
- [ ] T012 [US1] Verify default is OFF; toggling ON/OFF persists across reboot
(device proof after US6 flash)
**Checkpoint**: US1 code landed in tree (validated on device in US6 flash)
---
## Phase 4: User Story 4 — Restricted allow-list only (P1)
**Goal**: Non-authorized apps cannot effectively spoof
**Independent test**: Third-party test APK requesting FAKE_PACKAGE denied
- [ ] T013 [US4] Add allow-list enforcement tests/notes in
`docs/005-spoofing-runbook.md` (manual LAB procedure + expected deny)
- [ ] T014 [US4] Confirm implementation from T008 rejects non-listed packages
even with toggle ON (code review + LAB check after flash)
**Checkpoint**: Policy documented; enforcement in PMS path
---
## Phase 5: User Story 2 — Self-Check correct signatures (P1)
**Goal**: microG Self-Check marks correct signature for GmsCore + Phonesky
**Independent test**: Self-Check screenshots after grants
- [ ] T015 [US2] Ensure grant path works (permission UI / Self-Check tap) —
no missing `GrantFakeSignaturePermissionActivity` / unknown permission
- [ ] T016 [US2] Update `docs/004-lab-selfcheck.md` with spoofing steps and
expected green signature rows (post-`005`)
**Checkpoint**: Self-Check contract ready; device proof in US6
---
## Phase 6: User Story 3 — Play auth without API-console mismatch (P1)
**Goal**: Play sign-in without `UNREGISTERED_ON_API_CONSOLE`
**Independent test**: logcat during login
- [ ] T017 [US3] Document auth verification commands in
`specs/005-restricted-signature-spoofing/quickstart.md` (already drafted —
tighten failure signatures)
- [ ] T018 [US3] After flash (US6), capture logcat proving absence of
`UNREGISTERED_ON_API_CONSOLE` for Phonesky; store under
`/srv/rom/artifacts/005-spoofing/`
**Checkpoint**: Auth acceptance tied to US6 validation build
---
## Phase 7: User Story 5 — Survives without root (P2)
**Goal**: Spoofing works with no Magisk/KSU
**Independent test**: build props / `pm` show no Magisk
- [ ] T019 [US5] Confirm `005` product makefiles do not pull Magisk/KSU; note in
`docs/005-spoofing-runbook.md` and gate handoff
---
## Phase 8: User Story 6 — First working stack ship gate (P1)
**Goal**: One zip = REPLACE packaging + FGS privapp + spoofing; BeaconDB docs
**Independent test**: `quickstart.md` end-to-end on LAB
- [x] T020 [US6] `mka bacon -j8` on `rom-dev01`; log
`/srv/rom/logs/005-build/bacon.log`; verify intermediate Phonesky APK
`apksigner verify` (v2/v3) under `out/target/product/sunfish/`
*(SUCCESS 2026-09-15T14:27Z, 03:49:11; log
`bacon-20260915T103756Z.log`; zip SHA256
`1f08e00433e073e0b6525eb2539fe9011a291f34604a6917bf1836aecf03e383`;
Phonesky v2+v3 OK)*
- [x] T021 [US6] Stage zip + imgs + SHA256 + VERSIONS + spoofing notes to
`/srv/rom/artifacts/005-spoofing/<date>/` and rsync to
`.local-artifacts/005-spoofing/`
*(host `…/20260915-bacon/`; laptop got SHA+triage+packaging-check;
full zip remains on host)*
- [x] T022 [US6] Flash LAB only (`11231JEC207477`); confirm
`pm path` for gms/gsf/vending without remount
*(gms/gsf `/system_ext/…`; vending from zip then exact-alarm
`adb install -r` → `/data/app/…` until next bacon)*
- [x] T023 [US6] Enable spoofing toggle; grant FAKE_PACKAGE to gms + vending;
Self-Check correct signature green
*(`allow_signature_fake=1`; user 0+10 grants; operator Self-Check pass)*
- [x] T024 [US6] Play sign-in smoke; confirm no FGS VpaService crash loop; record
update-still-blocked as acceptable
*(installs OK both profiles after exact-alarm; UNREGISTERED count=0)*
- [x] T025 [P] [US6] Document BeaconDB location steps in
`docs/004-lab-selfcheck.md` + `quickstart.md` (URL
`https://api.beacondb.net`; no UnifiedNlp backends)
- [x] T026 [US6] Run full `specs/005-restricted-signature-spoofing/quickstart.md`
checklist; tick SC-001…SC-008
*(evidence `/srv/rom/artifacts/005-spoofing/20260915-us6-validation/`)*
**Checkpoint**: First working stack accepted on LAB
---
## Phase 9: Polish
- [x] T027 [P] Update `docs/axp-pro-parity.md` spoofing + microG/Phonesky rows
- [x] T028 [P] Finalize `docs/a15-a16-delta.md` with A15→A16 spoofing port notes
- [x] T029 Handoff: first stack done; next `006` banking rootless — **no** KSU
until after `006`
- [x] T030 Mark remaining `004` tasks superseded/closed where `005` US6 covered
them (`specs/004-microg-phonesky-integration/tasks.md` notes)
---
## Dependencies & Execution Order
### Phase dependencies
- Phase 1 → Phase 2 (foundational patches) → US1/US4 code → US2/US3/US5 docs →
**US6 build/flash validates everything**
- US6 is the integration gate; US1–US5 implementation lands before bacon when
possible, device proof in US6
### User story dependencies
| Story | Depends on |
|-------|------------|
| US1 toggle | T005–T007 (+ T011 Settings) |
| US4 allow-list | T008 |
| US2 Self-Check | T006–T009, US1 toggle |
| US3 Play auth | US2 grants working |
| US5 no root | Product wiring (Phase 2) |
| US6 ship | All above + T004 packaging |
### Parallel opportunities
- T002, T003, T004 after T001 started
- T009, T010 while T006–T008 in progress (XML vs frameworks)
- T025 docs parallel with T020 build
- T027, T028 polish parallel
---
## Parallel example: Foundational
```text
T009 Update Phonesky privapp digest + FAKE_PACKAGE in vendor/sunfish-a16/permissions/
T010 Align GmsCore sysconfig/default-permissions stubs in vendor/sunfish-a16/
T006 Port frameworks FAKE_PACKAGE + signingInfo spoof in frameworks/base/
```
---
## Implementation strategy
### MVP
1. Phase 1–2 (patches + XML)
2. US1 Settings toggle
3. US6 bacon + LAB flash proving toggle + Self-Check + Play auth
### Incremental
After MVP flash: tighten allow-list evidence (US4), BeaconDB doc polish (T025),
parity docs (T027–T029).
### Suggested first implement slice
**T001 → T004 → T005–T011 → T020–T026** (skip polish until green Self-Check).
---
## Notes
- Never flash GOLDEN; LAB serial only
- No Magisk/KSU in this feature
- Do not reintroduce UnifiedNlp backend APKs
- Prefer EXTENDROM attributable patches; record conflicts in delta doc
- User owns git commit/MR; agents only working tree / host build dirs
@@ -0,0 +1,41 @@
# Specification Quality Checklist: KernelSU-Next on sunfish (LAB)
**Purpose**: Validate specification completeness and quality before planning
**Created**: 2026-09-15
**Feature**: [spec.md](../spec.md)
## Content Quality
- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed
## Requirement Completeness
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified
## Feature Readiness
- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification
## Notes
- Product names required by constitution (KernelSU-Next, SusFS, Magisk-as-non-default,
TrickyStore-class *experiment*) appear as **capability choices**, not code design.
- Checklist item “non-technical stakeholders”: banking PASS/FAIL and LAB-only
constraints are written for the owner; kernel revision pinning is an audit need.
- SC-002 “≥95% checklist rows” assumes a concrete verification checklist in
`/speckit-plan` / tasks — acceptable at specify stage.
- Ready for `/speckit-plan`. **Yuh PASS is a hard-gate** (owner 2026-09-15):
without it, root is not justified vs NeoBackup + native call-rec / FLAG_SECURE.
@@ -0,0 +1,39 @@
# Contract: Yuh hard-gate (banking)
**Feature**: `007-kernelsu-sunfish`
**Device**: LAB `11231JEC207477` only
## Hard-gate
| App | Profile | Required outcome to accept `007` |
|-----|---------|----------------------------------|
| Yuh (`com.yuh`) | Island (work user) | **PASS** — usable authenticated session |
| Swissquote | Island | **PASS** — usable authenticated session; does not waive Yuh |
| N26 / Revolut | any | Non-blocking |
## Preconditions for a valid Yuh PASS attempt
1. Build includes KernelSU-Next pin under test.
2. Yuh UID(s) have **root deny** + **verified** mount hide (`ksu-hide-surface.md`).
3. Install path prefers Aurora → do not open → Island clone → first launch in
Island (AXP-like); document deviations.
4. Attestation/SusFS experiments in use are listed with revisions.
## FAIL handling
- Capture `keystore2` / KeyMint / RKP / Yuh UI error (compare `002`).
- Iterate SusFS/attestation pins; do not mark feature done.
- Exhausted options → `007` **incomplete or cancelled**; prefer stock phone for
banks rather than shipping root without Yuh.
## PASS evidence package
- Build id + date
- Profile user id
- Stack pins (KSU, SusFS, modules)
- Short log excerpt showing absence of prior `HARDWARE_TYPE_UNAVAILABLE` class
*or* explicit note if a different success path applies
- Stored under `/srv/rom/artifacts/007-kernelsu/…`
US5 exclusively owns acceptance. Both banks must pass on the same build/stack;
failure of either keeps the feature incomplete or triggers withdrawal of root.
@@ -0,0 +1,40 @@
# Contract: KernelSU hide / umount surface
**Feature**: `007-kernelsu-sunfish`
**Consumers**: LAB validation (`quickstart.md`), future `011` signed builds
## Root grant
| Rule | Requirement |
|------|-------------|
| Default | No app has root until explicitly allowed |
| Banking | `com.yuh`, `com.swissquote.android` → **deny** on all tested `userId`s |
| Proof | Manager UI alone insufficient; show failed `su`/root API from those UIDs |
## Mount hide
| Rule | Requirement |
|------|-------------|
| Mechanism | KernelSU umount / profile hide (and SusFS when enabled) |
| Proof | From banking (or probe) UID, sensitive module/overlay paths that appear
to a normal rooted UID are **absent** or inaccessible |
| `path_umount` | Must work on this 4.14 integration or be explicitly filed as blocker |
## Non-goals
- Magisk DenyList semantics as the production contract
- LSPosed/Zygisk hide
- Claiming “legitimate system” without Yuh PASS (see `banking-yuh-gate.md`)
## Mandatory evidence checklist
For each banking package × tested user ID, record PASS/FAIL, method and artifact:
- root grant denied;
- denied root access demonstrated;
- module mount visibility before/after recorded;
- configured unmount mechanism actually removes the test mount.
All rows must PASS. A probe must reproduce the target policy and namespace;
a different UID with unrelated settings is not evidence for the banking UID.
Unavailable evidence is unverified, not PASS. Optional SUSFS checks apply only
when enabled and must identify their tested paths.
+65
View File
@@ -0,0 +1,65 @@
# Data model: 007 KernelSU / banking gate
Conceptual entities for validation and artifact tracking (not a database schema).
## KernelSU pin
| Field | Meaning |
|-------|---------|
| `project` | KernelSU-Next (or documented ReSukiSU fallback) |
| `revision` | Exact git tag or commit |
| `kernel_base` | LOS 23.2 sunfish-compatible 4.14 tree identity |
| `boot_build_id` | `ro.lineage.version` / display id of accepting image |
| `status` | `poc` → `pinned` → `withdrawn` |
## SusFS pin (optional until enabled)
| Field | Meaning |
|-------|---------|
| `enabled` | boolean on accepting build |
| `revision` | tag/commit if separate |
| `soak_hours` | observed soak before claim |
| `status` | `off` / `trial` / `claimed` / `reverted` |
## App profile (per package × userId)
| Field | Meaning |
|-------|---------|
| `package` | e.g. `com.yuh` |
| `user_id` | 0 owner, 10 Island, … |
| `root_grant` | MUST be `deny` for banking targets |
| `umount_or_hide` | `verified` / `unverified` / `failed` |
| `evidence_path` | artifact log/screenshot |
## Banking session result
| Field | Meaning |
|-------|---------|
| `app` | Yuh \| Swissquote |
| `profile` | Island / owner |
| `build_id` | ROM build under test |
| `stack` | KSU ± SusFS ± attestation modules (names + pins) |
| `outcome` | `PASS` \| `FAIL` |
| `fail_class` | e.g. KeyMint/`HARDWARE_TYPE_UNAVAILABLE`, UI, network |
| `log_refs` | paths under `/srv/rom/artifacts/007-…` |
| `date` | ISO date |
**Rule**: Feature acceptance requires one **Yuh** and one **Swissquote** row on the same build/stack with
`outcome=PASS` on LAB Island under root-denied + hide-verified stack.
## Attestation experiment run
| Field | Meaning |
|-------|---------|
| `modules_or_toggles` | TrickyStore-class + microG HA notes |
| `baseline_ref` | `002` Yuh log pack |
| `result_ref` | new log pack |
| `delta` | same error class / improved / resolved |
## State transitions (feature)
```text
ksu_poc → profiles_verified → susfs_optional → attestation_trial
→ both_banks_pass (GATE) → 007_accepted
↘ either_bank_fail_exhausted → 007_incomplete_or_cancelled
```
+110
View File
@@ -0,0 +1,110 @@
# Implementation Plan: KernelSU-Next on sunfish (LAB)
**Branch**: `007-kernelsu-sunfish` | **Date**: 2026-09-15 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `/specs/007-kernelsu-sunfish/spec.md`
## Summary
Bring **KernelSU-Next** into the LineageOS 23.2 sunfish **4.14** kernel on LAB,
verify per-app / per-user **root denial + mount hide** for banking UIDs, then
layer **SusFS** and a **TrickyStore-class** attestation experiment so that
**Yuh PASS** (Island) becomes achievable. **Yuh PASS is a hard-gate**: without
it, `007` is incomplete/cancelled — root is not justified vs NeoBackup + native
`008`/`009` and a stock banking phone.
`006` rootless-Yuh-pass is **deferred** and does not block start. Swissquote PASS is also mandatory; Revolut/N26 non-blocking. GOLDEN never flashed.
## Technical Context
**Language/Version**: Linux 4.14 kernel (C); KernelSU-Next / SusFS integration;
Android userspace manager APK; bash runbooks on `rom-dev01`
**Primary Dependencies**: LOS 23.2 sunfish kernel tree; KernelSU-Next (non-GKI
4.14-capable pin); SusFS; TrickyStore-class (or documented equivalent); `005`
userspace stack; Island; `002` Yuh/KeyMint logs
**Storage**: Host `/srv/rom/` kernel outs, module ZIPs, secrets; artifacts under
`/srv/rom/artifacts/007-…`
**Testing**: LAB flash only; KSU manager grants; mount/root probes per UID;
Yuh/Swissquote Island sessions; `keystore2`/KeyMint logcat vs `002`
**Target Platform**: Pixel 4a LAB `11231JEC207477`; Android 16 / LOS 23.2
**Project Type**: Android ROM kernel + root stack + LAB validation
**Performance Goals**: Boot + daily stability; SusFS soak ≥24h if claimed
**Constraints**: LAB only; no Magisk production; no Zygisk/LSPosed default;
banking never gets root; attributable kernel commits; Yuh PASS mandatory
**Scale/Scope**: One kernel integration POC → pin revision → SusFS →
attestation experiment → banking hard-gate on LAB
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
| Gate | Status |
|------|--------|
| LAB only; never GOLDEN | pass |
| KernelSU-Next is project root (not Magisk production) | pass |
| Banking apps never receive root; profiles + verified umount | pass |
| SusFS only after KSU + profiles work | pass (phased US1→US3) |
| No Zygisk/LSPosed as initial architecture | pass |
| Small attributable, reversible changes | pass (pinned commits) |
| `006` before `007` in written order | **amended explicitly in constitution 0.2.0** — rootless Yuh gate deferred; owner-approved sequencing; current-ROM snapshot required (T039) |
| Hardening Graphene/AXP full ports | out of scope → `013` |
**Post-design**: Same. Yuh hard-gate is an owner product gate layered on top of
constitution Compatibility MVP (Yuh in Insular) — aligned, stricter on `007`
acceptance.
## Project Structure
### Documentation (this feature)
```text
specs/007-kernelsu-sunfish/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ ├── ksu-hide-surface.md
│ └── banking-yuh-gate.md
└── tasks.md # via /speckit-tasks (not this command)
```
### Host + tree
```text
# rom-dev01
/srv/rom/src/lineage/kernel/… # LOS 23.2 sunfish-compatible kernel
/srv/rom/src/lineage/… # product builds including boot.img
/srv/rom/artifacts/007-kernelsu/ # bacon outs, pins, Yuh log packs
/srv/rom/references/… # optional mirrors of KSU/SusFS/TrickyStore docs
# Meta repo
docs/002-*-*.md # Yuh/KeyMint baselines (read-only input)
docs/reference-sources.md §8–10 # community inspiration, not base
docs/a15-a16-delta.md # attestation notes as measured
```
**Structure Decision**: Kernel work lives on `rom-dev01` LOS tree; meta-repo
keeps specs, contracts, runbooks, and pin records. No Magisk tree. No megapatch
dumps in Git — pin URLs/commits + small project patches only.
## Complexity Tracking
| Violation / stretch | Why needed | Simpler alternative rejected because |
|---------------------|------------|--------------------------------------|
| Start `007` without closing `006` rootless Yuh | `002` shows Yuh fails with and without Magisk (KeyMint/RKP) | Waiting on rootless Yuh green has no known path |
| SusFS + TrickyStore-class in same feature as KSU | Owner: Yuh PASS mandatory; hide alone insufficient on GOLDEN | KSU-only “done” without Yuh → root not worth keeping |
| Yuh PASS hard-gate on EOL sunfish | Product rationale: else stock phone + `008`/`009` | Soft FAIL documentation wastes a root maintenance burden |
## Phase 0 / Phase 1
See `research.md`, `data-model.md`, `contracts/*`, `quickstart.md`.
## Remediation alignment — 2026-09-15
Constitution 0.2.0 explicitly defers `006`; T039 records our current rootless
LAB state before kernel changes, without requiring PASS. US5 alone owns banking
acceptance: both Yuh and Swissquote must pass on the same stack. US4 owns
attestation investigation/evidence, not a duplicate acceptance gate. Older
iodé/GOLDEN KeyMint logs are correlations, not proof of our ROM's failure cause.
T040 explicitly verifies the no-Zygisk/LSPosed constraint.
+64
View File
@@ -0,0 +1,64 @@
# Quickstart: 007 KernelSU + Yuh gate (LAB only)
**Device**: LAB `11231JEC207477` — never GOLDEN `11071JEC203839`
## Goal
Prove selective root + verified hide, then **Yuh and Swissquote PASS** in Island. Without both banks
passing, do not declare `007` done.
## Prerequisites
- Working `005` userspace stack on LAB (microG + Phonesky + spoofing)
- `007` boot image with pinned KernelSU-Next
- Island installed; Aurora available
- Baseline reading: `docs/002-golden-inventory.md`, `docs/002-lab-iodes-baseline.md`
## 1. Flash LAB
Use `docs/003-flash-lab.md` with the `007` artifact. Confirm:
```bash
SERIAL=11231JEC207477
adb -s "$SERIAL" shell getprop ro.lineage.version
# KernelSU manager present; grant root to one debug app only
```
## 2. Hide surface
1. Deny root for `com.yuh` and `com.swissquote.android` (all relevant users).
2. Enable umount/hide for those UIDs.
3. **Verify mounts** from a probe — see `contracts/ksu-hide-surface.md`.
## 3. SusFS (after step 2 green)
Enable SusFS per pin; confirm boot; re-check hide. Soak before claiming.
## 4. Attestation experiment
Apply TrickyStore-class (or equivalent) + any microG HA toggles under test.
Capture logcat around Yuh open (`keystore2`, KeyMint, RKP).
## 5. Banking (Island)
1. Aurora install Yuh/Swissquote → do not open on owner if following AXP order.
2. Clone into Island; first launch in Island.
3. Swissquote: must PASS a usable authenticated session.
4. Yuh: **must PASS** for feature acceptance (`contracts/banking-yuh-gate.md`).
## Pass criteria
- [ ] KSU selective root works
- [ ] Banking UIDs: root deny + verified hide
- [ ] SusFS claimed only if stable
- [ ] **Yuh PASS** in Island with evidence under `/srv/rom/artifacts/007-…`
- [ ] Swissquote PASS on the same stack
- [ ] No Magisk production image
## Failure
If either bank remains unusable after reasonable SusFS/attestation
iterations: stop, keep artifacts, mark `007` blocked — consider stock phone for
banks; do not ship “root without Yuh” as success.
Before any rooted flash, complete T039. Before acceptance complete T040.
+106
View File
@@ -0,0 +1,106 @@
# Research: 007 KernelSU-Next on sunfish
## Decision: Defer `006` rootless-Yuh gate; start `007` now
**Decision**: Do not block on a separate `006` “Yuh green without root”. Absorb
banking regression into `007`. A rootless snapshot on the current `005` LAB image is required before root;
its PASS/FAIL outcome is informational and does not block experiments.
**Rationale**: GOLDEN (Magisk) and LAB iodé (no Magisk, locked BL) both show Yuh
KeyMint/`HARDWARE_TYPE_UNAVAILABLE`. Rootless is not a known fix.
**Alternatives**: Force `006` first — rejected. Cancel banking on ROM entirely
before trying KSU stack — premature; try `007` experiment first.
## Decision: Yuh PASS is mandatory to accept `007`
**Decision**: Feature closes only when both Yuh and Swissquote work in Island on LAB under
root-denied + verified hide (+ SusFS/attestation stack as needed). Honest
residual FAIL ⇒ `007` incomplete or cancelled — not “KSU done, Yuh later”.
**Rationale**: Owner (2026-09-15): without Yuh, root adds little vs NeoBackup +
native call recording / FLAG_SECURE; denylist-style legitimacy alone is not
enough reason to carry KSU. Stock handset remains interim fallback, not
acceptance.
**Alternatives**: Soft gate / document FAIL — rejected by owner.
## Decision: Prefer ReSukiSU + TEESimulator for hard-bank PoC (2026-09-15)
**Decision**: Align LAB banking experiments with torukk/abu field results:
ReSukiSU over KernelSU-Next when Revolut-class detection matters; **TEESimulator-RS**
instead of TrickyStore; document Zygisk Next + PIF inject as possible LAB-only
exception if required for the Yuh/Swissquote gate.
**Rationale**: Owner overlap with that project; XDA 2026-08-22; constitution
already names ReSukiSU as evaluable fork.
**Alternatives**: KernelSU-Next-only in-tree first — still useful as build
practice, weaker evidence for hard banks.
## Decision: Profiles + verified umount before SusFS
**Decision**: Order: (1) KSU boots + selective root, (2) banking UIDs denied +
**mount evidence** of hide/`path_umount`, (3) SusFS, (4) attestation modules.
**Rationale**: Constitution: SusFS only after KernelSU and application profiles
work. GUI toggle alone is insufficient.
## Decision: TrickyStore-class → prefer TEESimulator-RS
**Decision**: Attestation experiment track prefers **TEESimulator-RS** (TrickyStore
compatible drop-in) per owner + torukk; select banking packages only.
**Rationale**: Community recommendation over original TrickyStore; same slot in
the stack.
## Decision: Island remains managed-profile vehicle
**Decision**: Keep Island (already on LAB/GOLDEN) for Yuh/Swissquote unless
planning discovers a blocking incompatibility; then document substitute.
**Rationale**: Matches AXP oracle workflow and existing LAB user 10.
## Decision: No Zygisk/LSPosed in initial `007`
**Decision**: Out of default scope. Prefer native ROM features for call-rec /
FLAG_SECURE later (`008`/`009`).
**Rationale**: Constitution.
## Decision: Full Graphene/AXP kernel hardening stays `013`
**Decision**: `007` only patches kernel as required for KSU / SusFS / umount.
Do not import broad hardening series here.
**Rationale**: Constitution progress order; reduces blast radius while chasing Yuh.
## Remediation alignment — 2026-09-15
Constitution 0.2.0 explicitly defers `006`; T039 records our current rootless
LAB state before kernel changes, without requiring PASS. US5 alone owns banking
acceptance: both Yuh and Swissquote must pass on the same stack. US4 owns
attestation investigation/evidence, not a duplicate acceptance gate. Older
iodé/GOLDEN KeyMint logs are correlations, not proof of our ROM's failure cause.
T040 explicitly verifies the no-Zygisk/LSPosed constraint.
## Concrete attestation candidates (not yet selected or validated)
- TrickyStore: https://github.com/5ec1cff/TrickyStore — certificate-chain
intervention; current releases closed-source. Record auditability limitations.
- TEESimulator: https://github.com/JingMatrix/TEESimulator — open-source
software KeyMint/key-attestation simulation; candidate alternative, not
authentic hardware repair. Requires external signing material not bundled.
- TEESimulator-RS: https://github.com/Enginex0/TEESimulator-RS — related
alternative; independently verify implementation, dependencies and A16 support.
- BootloaderSpoofer: https://modules.lsposed.org/module/es.chiteroman.bootloaderspoofer/
— LSPosed/Xposed approach, outside FR-009 default; not an approved candidate
without a separate explicit scope decision.
T022 selects one concrete candidate before deployment: upstream URL, immutable
commit or release with binary hash, license/source availability, dependencies,
key-material provenance/availability and rollback. Do not collect keys from
unknown sources or commit signing secrets. Evaluate candidates separately, not
as a combined stack. No candidate is verified for Yuh on our sunfish yet.
KernelSU/SUSFS and Play Integrity modules are not interchangeable substitutes
for Key Attestation. A simulator does not itself create a trusted signing chain.
+269
View File
@@ -0,0 +1,269 @@
# Feature Specification: KernelSU-Next on sunfish (LAB)
**Feature Branch**: `007-kernelsu-sunfish`
**Created**: 2026-09-15
**Status**: In progress (LAB)
**Session handoff**: `docs/session-20260916-handoff.md` (2026-09-16)
**Input**: After `005` first Google-compat stack works on LAB, skip forcing a
rootless Yuh green gate. Prioritize **ReSukiSU** (not Magisk; KSU-Next only as
history) on the LineageOS 23.2 sunfish kernel, verified hide for banking UIDs,
then SusFS / TEESimulator-class LAB experiments aimed at Yuh KeyMint/RKP
failures already logged in `002`. LAB only; never GOLDEN. Personal stock
handset for banks remains an optional operator fallback outside this feature.
**Spine reminder:** product path is **our** LOS 23.2 (`003`→`004`/`005` bacon)
then `007`. Community abu/torukk ROMs and Downloads boots are reference only.
## Context / sequencing vs `006`
Constitution originally ordered a **rootless** banking baseline (`006`) before
root (`007`). Evidence already shows:
| Device | Stack | Swissquote | Yuh |
|--------|-------|------------|-----|
| GOLDEN | AXP + Magisk DenyList | PASS (owner) | FAIL — HA / “certificate invalid”; KeyMint/`HARDWARE_TYPE_UNAVAILABLE` |
| LAB (iodé A16) | No Magisk; BL locked | — | FAIL — same KeyMint/RKP class |
So a “rootless Yuh must pass” gate is **not** a credible start condition.
This feature **absorbs** the banking regression that `006` would have owned:
measure owner + Island UIDs on **our** ROM with root hidden. A separate `006`
spec may stay deferred or become a thin historical note — it must not block
`007` work.
**Explicitly later (do not pull into this feature):** call recording (`008`),
`FLAG_SECURE` (`009`), AXP UX leftovers (`010`), project signing/AVB (`011`),
incremental Graphene/AXP/Divest **hardening** ports (`012`–`013`).
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Bootable rooted LAB image (Priority: P1)
As the developer, I can flash a LAB build whose kernel includes KernelSU-Next
compatible with non-GKI Linux 4.14 / sunfish, boot to the lock screen, and
confirm root is available only through the KernelSU manager to apps I approve.
**Why this priority**: Without a working integrated root path, hide/SusFS/banking
experiments cannot run on *our* ROM.
**Independent Test**: Flash LAB only; manager shows installed; `su` works for one
approved test app and fails for an unapproved app.
**Acceptance Scenarios**:
1. **Given** a `007` LAB flash, **When** the device boots, **Then** telephony,
Wi‑Fi, and basic UI work at least as well as the last `005` image.
2. **Given** KernelSU manager, **When** root is granted to a single debug app,
**Then** that app can obtain root and an unapproved app cannot.
3. **Given** documentation, **When** reviewed, **Then** the exact KernelSU-Next
revision (tag/commit) used for the accepted POC is recorded for reproducibility.
---
### User Story 2 - Banking apps never get root; mounts hidden (Priority: P1)
As the developer, Yuh and Swissquote (owner and Island/work profile UIDs) are
configured so they **never** receive root, and module/overlay mounts that would
expose root or custom ROM fingerprints to those UIDs are removed or hidden —
verified by a real check, not only a UI toggle.
**Why this priority**: Constitution root architecture; Magisk DenyList on GOLDEN
is oracle-only, not our production model.
**Independent Test**: Per-UID checks (process view of mounts / root binaries)
for banking packages on user 0 and work profile; screenshots/logs stored under
artifacts.
**Acceptance Scenarios**:
1. **Given** Yuh and Swissquote installed (Aurora → Island workflow preferred),
**When** root grant lists are reviewed, **Then** those packages are denied
root on every relevant user ID.
2. **Given** a module or overlay that would be visible to a normal app,
**When** the banking UID is inspected, **Then** that mount is absent or
otherwise hidden per the verified umount/profile mechanism.
3. **Given** only a manager “hide” toggle without mount evidence, **When**
validation runs, **Then** the story is **not** marked pass.
---
### User Story 3 - SusFS after profiles work (Priority: P2)
As the developer, once KernelSU profiles and verified hide work, I can enable a
SusFS-capable kernel/config on LAB and confirm the device still boots and that
selected path-hiding behaviours are measurable for banking UIDs.
**Why this priority**: Needed for hard banks on community sunfish reports; must
not destabilize before basic KSU works. Constitution: SusFS only after KernelSU
and application profiles work.
**Independent Test**: LAB boot + documented SusFS enablement + before/after
visibility check for at least one sensitive path from a banking or probe UID.
**Acceptance Scenarios**:
1. **Given** US1–US2 green, **When** SusFS is enabled on LAB, **Then** the
device boots and KernelSU manager still functions.
2. **Given** SusFS on, **When** a probe from a denied banking UID runs,
**Then** at least one previously visible root/custom indicator is hidden
(evidence logged).
3. **Given** SusFS causes repeated spontaneous reboot or severe thermal
instability, **When** triage completes, **Then** SusFS is reverted or pinned
to a known-good revision before continuing banking claims.
---
### User Story 4 - Attestation / KeyMint experiment track (Priority: P1)
As the developer, I can run a **LAB-only** attestation-compatibility experiment
(TrickyStore-class or equivalent, plus any required microG hardware-attestation
toggles) aimed at the Yuh failure mode already captured in `002` (KeyMint /
`HARDWARE_TYPE_UNAVAILABLE` / “certificate invalid”), without modifying GOLDEN.
**Why this priority**: Root hide alone did not fix Yuh on GOLDEN or iodé LAB;
attestation is the open hypothesis. **Yuh PASS is mandatory** for feature
acceptance — otherwise root is not worth carrying vs NeoBackup + native
`008`/`009` and a stock banking phone.
**Independent Test**: Reproduce Yuh launch with logcat focused on `keystore2` /
KeyMint / RKP; compare to `002` baselines; diagnostic results required with artifact paths; US5 owns PASS.
**Acceptance Scenarios**:
1. **Given** `002` Yuh failure signatures, **When** the experiment stack is
applied on LAB, **Then** a new log capture exists that either shows the same
class of error or a clear change.
2. **Given** a candidate experiment, **When** the same attestation probe and
bank attempt are repeated, **Then** record the before/after result, limitations
and rollback outcome. US4 owns diagnostic evidence, not banking acceptance.
3. **Given** persistent KeyMint/RKP messages, **When** results are interpreted,
**Then** do not infer causality or failure from those messages alone. US5
exclusively owns the end-to-end banking acceptance decision.
---
### User Story 5 - Banking regression on our ROM (Priority: P1)
As the project owner, after hide (and any accepted SusFS/attestation experiments)
I have a written regression for **Swissquote** and **Yuh** inside Island on the
`007` LAB image: install path, profile UIDs, biometrics if used, and outcome.
Revolut/N26 remain non-blocking.
**Why this priority**: Compatibility hard-gates; replaces the deferred `006`
oracle for “does banking work on our stack”.
**Independent Test**: End-to-end on LAB only; evidence under
`/srv/rom/artifacts/007-…` (and optional laptop rsync). No GOLDEN flash/wipe.
**Acceptance Scenarios**:
1. **Given** Island (or documented equivalent), **When** Swissquote is used in
the work profile with root denied + hide verified, **Then** Swissquote MUST PASS with a usable authenticated session, date and build id.
2. **Given** the same setup for Yuh, **When** login/attestation is attempted,
**Then** the session MUST **PASS**; an unusable session means
`007` is not accepted.
3. **Given** Magisk, **When** proposed as production root, **Then** it is
rejected; Magisk A/B diagnostic builds are allowed only as temporary
reference per constitution.
---
### Edge Cases
- KernelSU-Next revision incompatible with 4.14 → evaluate alternate pin /
ReSukiSU only as documented POC fork, not silent default change.
- Work-profile UIDs differ from owner → hide/root denial must be tested on both.
- `path_umount` missing/broken on 4.14 → story 2 fails until backport/verify.
- Attestation modules break Play/microg → revert experiment; keep iterating
until both banks PASS or declare `007` blocked/cancelled.
- Operator chooses a separate stock phone for banks → interim fallback only;
does **not** satisfy the Yuh hard-gate on LAB.
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: Project MUST integrate KernelSU-Next into the LineageOS
23.2–compatible sunfish kernel built with the ROM (not Magisk as production
root).
- **FR-002**: Root MUST be grantable only to explicitly approved apps; banking
packages (at least Yuh and Swissquote) MUST never receive root on owner or
work-profile user IDs used in the regression.
- **FR-003**: Module/overlay mounts MUST be removable or hidden from selected
app processes, with verification beyond manager UI toggles; `path_umount` (or
equivalent) support on this kernel MUST be confirmed or explicitly failed.
- **FR-004**: The accepted KernelSU-Next revision MUST be pinned and recorded
(tag/commit) for reproducible builds.
- **FR-005**: SusFS MUST only be enabled after FR-001–FR-003 work on LAB; if
enabled, boot stability and at least one measurable hide effect MUST be
evidenced.
- **FR-006**: Evaluate an attestation candidate with exact source URL, immutable
revision or release plus binary hash, dependencies, auditability, baseline,
before/after evidence and rollback. KeyMint/RKP causality remains a hypothesis.
Banking acceptance is owned solely by FR-007 / US5.
- **FR-007**: Both Yuh and Swissquote MUST PASS usable authenticated sessions in
the LAB managed profile on the same accepted stack with root denied and hide
verified. Failure of either bank leaves `007` incomplete or cancelled.
N26/Revolut are non-blocking.
- **FR-008**: All flashing, kernel experiments, and attestation modules MUST
target LAB (`11231JEC207477`) only; GOLDEN MUST remain read-only oracle.
- **FR-009**: Zygisk/LSPosed MUST NOT be introduced as the initial architecture
unless a later feature proves no native alternative (out of default `007`
scope).
- **FR-010**: Feature docs MUST state that deferred `006` rootless-Yuh-pass is
not a prerequisite, and must link `002` Yuh/Swissquote evidence paths.
### Key Entities
- **LAB device**: Experimental sunfish; only flash target for `007`.
- **KernelSU profile**: Per-app (and per-user) root grant / umount policy.
- **Banking target apps**: Yuh (`com.yuh`), Swissquote (`com.swissquote.android`).
- **Managed profile**: Island (or equivalent) work user hosting banking apps.
- **Attestation experiment stack**: Optional LAB modules/toggles aimed at
hardware attestation / KeyMint behaviour.
- **Pinned root revision**: Exact KernelSU-Next (and SusFS, if used) identifiers.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: LAB boots a `007` image with working selective root within one
flash cycle after the accepted kernel build.
- **SC-002**: For Yuh and Swissquote on every tested user ID, root grant is
denied and mount-hide is evidenced (not toggle-only) for every applicable contract checklist row, for both packages and every tested user ID; no failed or unverified mandatory row is allowed.
- **SC-003**: **Yuh PASS** on LAB Island under the `007` stack (root denied +
verified hide + accepted SusFS/attestation experiments), with build id and
evidence — required to close the feature.
- **SC-004**: Swissquote MUST PASS a usable authenticated session on the same
image/stack as SC-003, with dated evidence; both are required for acceptance.
- **SC-005**: If SusFS is claimed in the release notes for this phase, LAB
completes ≥24h soak without SusFS-driven reboot loops, or SusFS is withdrawn
from the claim.
- **SC-006**: Artifact package contains before/after attestation probe results
and correlated banking logs, including unresolved messages and limitations.
Absence of a log string alone is not proof of successful attestation.
- **SC-007**: No Magisk-based production image is published as the project
default root path.
## Assumptions
- `005` stack (microG + patched Phonesky + restricted spoofing) remains the
userspace base on LAB before/while bringing up `007`.
- Island (already used on LAB/GOLDEN) remains the managed-profile vehicle unless
a documented substitute is chosen during implementation planning.
- If EOL sunfish cannot satisfy Yuh even with SusFS/TrickyStore-class stacks,
**`007` stays incomplete** (or is explicitly cancelled) — not “done with Yuh
documented FAIL”. Rationale: without Yuh, root adds little over NeoBackup +
native `008`/`009`; a stock handset becomes the banking plan instead.
- Operator may still use a separate stock phone as interim fallback; that does
not count as `007` acceptance.
- Full GrapheneOS/AXP/Divest kernel hardening ports remain `013`, not this
feature — `007` only changes the kernel as needed for KSU/SusFS/umount.
- Community sunfish LOS+KSU+SusFS reports are inspiration, not a drop-in base.
## Out of scope
- Flashing or modifying GOLDEN.
- Magisk/Magia as default project root.
- Making Revolut/N26 hard-gates.
- Call recording, FLAG_SECURE, WebView provider work, project AVB lock.
- Purchasing or provisioning a second physical phone (operator choice).
+252
View File
@@ -0,0 +1,252 @@
# Tasks: KernelSU-Next on sunfish (LAB) + Yuh/Swissquote gate
**Input**: Design docs from `/specs/007-kernelsu-sunfish/`
**Prerequisites**: `005` stack on LAB; `002` Yuh/KeyMint baselines; constitution
KernelSU-Next (not Magisk production)
**Organization**: Priority = **Swissquote + Yuh** on LAB. KSU/SusFS/TrickyStore
exist to serve that gate. If the gate cannot be met after the experiment track,
**cancel root** and pivot to native `008`/`009` (+ optional stock phone) — do
not ship “root without Yuh” as success.
**Out of scope**: GOLDEN flash; Magisk production; Revolut/N26 hard-gates;
full Graphene/AXP kernel hardening (`013`).
## Format: `[ID] [P?] [Story] Description`
- **[P]**: Can run in parallel (different files, no incomplete deps)
- **[Story]**: US1…US5 from `spec.md`
## Path conventions
- Meta: `docs/`, `specs/007-kernelsu-sunfish/`
- Host: `/srv/rom/src/lineage/` (kernel + boot), `/srv/rom/artifacts/007-kernelsu/`,
`/srv/rom/logs/007-build/`
---
## Phase 1: Setup
**Purpose**: Artifact dirs, baseline index, pin stubs
- [x] T001 Create `/srv/rom/artifacts/007-kernelsu/` and
`/srv/rom/logs/007-build/` on `rom-dev01` (android-build writable)
- [x] T002 [P] Add `docs/007-kernelsu-runbook.md` stub (LAB-only; pin table;
Yuh hard-gate; cancel-root criteria) in meta repo
- [x] T003 [P] Index `002` Yuh evidence paths in
`docs/007-kernelsu-runbook.md` (GOLDEN + LAB iodé KeyMint refs from
`docs/002-golden-inventory.md` / `docs/002-lab-iodes-baseline.md`)
- [x] T004 [P] Confirm LAB still on `005` userspace
(`pm path` gms/gsf/vending; spoof toggle) — note build id in
`/srv/rom/artifacts/007-kernelsu/preflight.txt`
- [x] T039 Before T009/T011, capture a rootless snapshot of the current LAB ROM:
both bank versions/outcomes, build type, microG/Phonesky versions and
attestation probe/logs under `pre-root/`. FAIL is allowed; record actual
results rather than inferring them from iodé/GOLDEN. Document Island as
the current managed-profile vehicle in `docs/007-kernelsu-runbook.md`.
*(2026-09-15: banks not installed → N/A; see pre-root/OUTCOMES.txt)*
---
## Phase 2: Foundational (blocks all stories)
**Purpose**: Choose 4.14-capable KernelSU-Next revision; locate LOS kernel tree;
no banking claims yet
**⚠️ CRITICAL**: No SusFS/attestation/Yuh PASS claims until KSU boots on LAB
- [x] T005 Inventory LOS 23.2 sunfish kernel path on
`/srv/rom/src/lineage/` (defconfig, branch); record in
`docs/007-kernelsu-runbook.md`
*(`kernel/google/msm-4.14` @ `28f9290ae067…`; sunfish_defconfig)*
- [x] T006 Select KernelSU-Next revision compatible with non-GKI 4.14; document
candidates + choice in `docs/007-kernelsu-runbook.md` (ReSukiSU only as
explicit fallback)
*(`legacy` branch; manual hooks preferred over kprobes)*
- [x] T007 Mirror/checkout chosen KernelSU-Next sources under
`/srv/rom/references/kernelsu-next/` (or documented path); record commit
*(@ `a54e4fa46c6c…`)*
- [x] T008 Draft integration notes (what files touch kernel vs boot.img) in
`docs/007-kernelsu-runbook.md` — attributable commits only, no megapatch
**Checkpoint**: Ready to integrate and build boot (US1)
---
## Phase 3: User Story 1 — Bootable rooted LAB image (P1) 🎯 MVP slice
**Goal**: LAB boots with KernelSU-Next; selective root works
**Independent test**: Manager present; one approved app gets root; unapproved
does not; telephony/Wi‑Fi OK
- [ ] T009 [US1] Integrate KernelSU-Next into sunfish kernel tree under
`/srv/rom/src/lineage/`; build boot/kernel; log
`/srv/rom/logs/007-build/ksu-poc.log`
- [ ] T010 [US1] Stage boot/images + SHA256 to
`/srv/rom/artifacts/007-kernelsu/<date>-ksu-poc/`
- [ ] T011 [US1] Flash LAB only (`11231JEC207477`); confirm boot +
`ro.lineage.version`; install/open KSU manager
- [ ] T012 [US1] Grant root to one debug app; deny a second; capture evidence
under `/srv/rom/artifacts/007-kernelsu/<date>-ksu-poc/root-grant/`
- [ ] T013 [US1] Pin exact KernelSU-Next revision in
`docs/007-kernelsu-runbook.md` + artifact `PIN.txt`
**Checkpoint**: US1 green before profiles/SusFS
---
## Phase 4: User Story 2 — Banking never root; mounts hidden (P1)
**Goal**: Yuh/Swissquote UIDs denied + **verified** umount/hide
**Independent test**: `contracts/ksu-hide-surface.md` — not toggle-only
- [ ] T014 [US2] Configure KSU profiles: deny root for `com.yuh` and
`com.swissquote.android` on user 0 and Island user; document steps in
`docs/007-kernelsu-runbook.md`
- [ ] T015 [US2] Enable umount/hide for those UIDs; verify `path_umount` (or
equivalent) works on this 4.14 build — note pass/fail in
`/srv/rom/artifacts/007-kernelsu/<date>-hide/umount.txt`
- [ ] T016 [US2] From banking or probe UID, capture mount/root visibility
before/after hide →
`/srv/rom/artifacts/007-kernelsu/<date>-hide/mounts/`
- [ ] T017 [US2] Mark `contracts/ksu-hide-surface.md` checklist rows against
real evidence (update runbook if contract gaps)
**Checkpoint**: Hide verified before SusFS
---
## Phase 5: User Story 3 — SusFS after profiles (P2)
**Goal**: SusFS on LAB; measurable hide; no reboot loop
**Independent test**: Boot + one path hidden; soak before claim
**Note (2026-09-16):** T018 **build** completed early (API bridge) *before* US2
hide verification — process exception. Boot artifact
`boot-resukisu-susfs-20260916T004038Z.img` is **trial only**. Prefer LAB on
`boot-resukisu-our-20260915T183729Z` until US2 green; then re-attempt T019.
abu/torukk boots are reference-only — see `docs/session-20260916-handoff.md`.
- [x] T018 [US3] Integrate SusFS into the pinned kernel path; build; log
(host detach SusFS boot 20260916T004038Z; sha `455a77a6…`)
- [ ] T019 [US3] Flash LAB; confirm boot + KSU still works; record SusFS pin in
`docs/007-kernelsu-runbook.md`
- [ ] T020 [US3] Re-verify banking UID hide with SusFS on → artifact
`/srv/rom/artifacts/007-kernelsu/<date>-susfs/`
- [ ] T021 [US3] Soak ≥24h or document revert if unstable (SC-005); update
SusFS status `trial`/`claimed`/`reverted` in runbook
**Checkpoint**: SusFS stable or withdrawn before attestation claims that depend
on it
---
## Phase 6: User Story 4 — Attestation / KeyMint experiment (P1)
**Goal**: TrickyStore-class (or equiv.) vs `002` Yuh KeyMint failure
**Independent test**: New log pack; progress toward Yuh PASS
- [ ] T022 [US4] Choose attestation experiment stack (TrickyStore-class or
documented equivalent + microG HA toggles); record source URL, immutable commit or release and binary SHA256, dependencies, key-material requirements, auditability and rollback in
`docs/007-kernelsu-runbook.md` **before T023**
- [ ] T023 [US4] Apply stack on LAB; capture `keystore2`/KeyMint/RKP logcat on
Yuh open → `/srv/rom/artifacts/007-kernelsu/<date>-attest/`
- [ ] T024 [US4] Diff against `002` failure class; write short delta note in
artifact `DELTA.md` (same / improved / resolved)
- [ ] T025 [US4] Iterate one candidate at a time based on measured probe/banking outcomes; do not use a log string alone as the failure criterion; keep each attempt under dated artifact dirs — do
not mark feature done on FAIL
**Checkpoint**: Diagnostic evidence ready for US5; persistent log messages
alone do not determine acceptance. Exhausted unsuccessful bank attempts lead
to Phase 9 or an incomplete feature.
---
## Phase 7: User Story 5 — Banking regression / hard-gate (P1)
**Goal**: **Yuh and Swissquote PASS** in Island
**Independent test**: `contracts/banking-yuh-gate.md` + `quickstart.md`
- [ ] T026 [US5] Install path: Aurora → (prefer no owner open) → Island clone
for Yuh + Swissquote; note UIDs in
`/srv/rom/artifacts/007-kernelsu/<date>-banking/layout.txt`
- [ ] T027 [US5] Swissquote Island session → **PASS required** + evidence under
`…/<date>-banking/swissquote/`
- [ ] T028 [US5] Yuh Island session → **PASS required**; evidence under
`…/<date>-banking/yuh/` (build id, stack pins, log excerpt)
- [ ] T040 Before T029 acceptance, verify FR-009: installed packages, root
module inventory, relevant processes/mounts and build configuration show
no Zygisk/LSPosed integration. Store evidence; a package-name check alone
is insufficient. Any proposed exception requires a spec decision first.
- [ ] T029 [US5] If both banks PASS: fill `data-model.md` banking row + tick
`specs/007-kernelsu-sunfish/quickstart.md` pass criteria
- [ ] T030 [US5] If either bank FAILS after T022–T025 exhausted: do **not** accept
`007`; proceed to Phase 9 cancel-root tasks
**Checkpoint**: Feature accepted only with both banks PASS
---
## Phase 8: Polish (only if both banks PASS)
- [ ] T031 [P] Update `docs/axp-pro-parity.md` root / DenyList / Yuh / Swissquote
rows to reflect KSU-Next + LAB results
- [ ] T032 [P] Update `docs/a15-a16-delta.md` attestation/`007` measured notes
- [ ] T033 Handoff note in `docs/007-kernelsu-runbook.md`: next may be `008`
call recording / `009` FLAG_SECURE; full hardening still `012`–`013`
- [ ] T034 Mark deferred `006` as superseded-by-`007` (or thin stub) in
`specs/` / roadmap note — no fake rootless Yuh success
---
## Phase 9: Cancel-root pivot (only if either banking hard-gate fails)
**Purpose**: Owner priority — without Yuh/Swissquote success, discard project
root and keep native requirements instead
- [ ] T035 Write `/srv/rom/artifacts/007-kernelsu/CANCEL-ROOT.md` (attempts,
last pins, KeyMint residual, recommendation: stock phone for banks)
- [ ] T036 Update `docs/007-kernelsu-runbook.md` + `docs/axp-pro-parity.md`:
root path **withdrawn**; Magisk/KSU not production
- [ ] T037 [P] Stub or specify next focus: `008` call recording + `009`
FLAG_SECURE (native, no LSPosed) — Spec Kit when owner starts them
- [ ] T038 Leave LAB on last known-good **non-root** (`005`) image or document
re-flash steps in runbook
---
## Dependencies
```text
Setup T001–T004 + T039
→ Foundational T005–T008
→ US1 T009–T013 (KSU boot)
→ US2 T014–T017 (hide)
→ US3 T018–T021 (SusFS)
→ US4 T022–T025 (attest)
→ US5 T026–T030 + T040 (both-bank gate)
↘ PASS → Polish T031–T034
↘ FAIL → Cancel-root T035–T038
```
- US3 may be skipped only if US4/US5 PASS without SusFS (document why)
- US4/US5 are the product priority; US1–US2 are enablers
## Parallel opportunities
- T002, T003, T004 after T001
- T031, T032 after both banks PASS
- T037 anytime during cancel-root phase
## MVP
**Minimum useful slice**: T001–T013 (KSU boots on LAB).
**Product MVP**: T027 Swissquote PASS and T028 Yuh PASS on the same stack.
Either unresolved failure → Phase 9 or remain incomplete, never “007 done”.
## Suggested next command after tasks
`/speckit-analyze` (optional) → `/speckit-implement` starting at T001 on
`rom-dev01` (kernel work is host-side; LAB flash when boot ready).
@@ -0,0 +1,27 @@
# Specification Quality Checklist: Call recording + FLAG_SECURE
**Purpose**: Validate specification completeness before planning
**Created**: 2026-09-16
**Feature**: [spec.md](../spec.md)
## Content Quality
- [x] No implementation-only jargon in Success Criteria
- [x] Requirements are testable
- [x] Scope (global toggles only) is explicit
- [x] Non-goals documented
- [x] LAB-only constraint present
## Completeness
- [x] User stories cover call-rec and FLAG_SECURE
- [x] Secure setting names proposed
- [x] Default OFF behaviour stated
- [x] EXTENDROM / AXP GOLDEN call-rec references noted in design doc
- [x] FLAG_SECURE scope is global only (per-app not planned)
## Notes
Ready for `/speckit-plan` when owner starts implementation on `rom-dev01`.
Investigate Dialer gate on LOS 23.2 before coding call-rec (A15 EXTENDROM
overlay dirs were empty; A11 is reference only).
@@ -0,0 +1,32 @@
# Contract: Call recording surface
**Feature**: `008-call-rec-flag-secure`
**Consumers**: LAB validation (`quickstart.md`)
## Secure setting
| Item | Value |
|------|--------|
| Key | `Settings.Secure.extendrom_call_recording` |
| Values | `null`/`0` = stock country rules; `1` = ignore country deny |
| Readback | `adb shell settings get secure extendrom_call_recording` |
| Write | Developer Options switch or `settings put secure … 0\|1` |
## Dialer behaviour
| Condition | Expected |
|-----------|----------|
| Feature off / setting `0` | `CallRecorder.canRecordInCurrentCountry()` uses `call_record_states.xml` |
| Setting `1` and recording enabled | `canRecordInCurrentCountry()` true → record control available |
| `call_recording_enabled` false | Still disabled (override does not invent recording capability) |
## UI
- Developer Options entry with legal responsibility summary (EXTENDROM-style).
- Reset to `0` when Developer Options master is disabled.
## Evidence
- Setting readback 0/1.
- In-call record start with setting `1` produces a file; note path under
`/srv/rom/artifacts/008-call-rec-flag-secure/call-rec/`.
@@ -0,0 +1,34 @@
# Contract: FLAG_SECURE / screenshot surface
**Feature**: `008-call-rec-flag-secure`
**Consumers**: LAB validation (`quickstart.md`)
## Secure setting
| Item | Value |
|------|--------|
| Key | `Settings.Secure.disable_secure_windows` (AOSP `DISABLE_SECURE_WINDOWS`) |
| Values | `null`/`0` = stock `FLAG_SECURE`; `1` = global capture override |
| Readback | `adb shell settings get secure disable_secure_windows` |
| UI preference key | `allow_flag_secure_override` (writes the Secure key above) |
| Scope | **Global** (all apps); per-app lists not planned |
| Note | WMS only applies this on **debuggable** builds (`ro.debuggable`) |
## Capture behaviour
| Condition | Expected |
|-----------|----------|
| Setting `0`, secure window | System screenshot / screen record blocked as stock |
| Setting `1`, same window | System screenshot / screen record succeeds |
| Setting `1`, DRM/HW buffer edge cases | May still fail — document if observed |
## UI
- Developer Options switch; short summary that this weakens app privacy.
- Reset to `0` when Developer Options master is disabled.
## Evidence
- Setting readback 0/1.
- Before/after screenshots (or failure toast) stored under
`/srv/rom/artifacts/008-call-rec-flag-secure/flag-secure/`.
@@ -0,0 +1,52 @@
# Data model: `008-call-rec-flag-secure`
## Entities
### SecureSettingCallRec
| Field | Type | Rules |
|-------|------|--------|
| key | string | `extendrom_call_recording` |
| value | int | `0` (default) or `1` |
| namespace | Secure | `Settings.Secure` |
**Transitions**: DO switch ON → `1`; OFF → `0`; Developer Options master
disabled → force `0`.
### SecureSettingFlagSecure
| Field | Type | Rules |
|-------|------|--------|
| key | string | `disable_secure_windows` (DO pref `allow_flag_secure_override`) |
| value | int | `0` (default) or `1` |
| namespace | Secure | `Settings.Secure` |
**Transitions**: same as call-rec.
### DialerCountryPolicy
| Field | Type | Rules |
|-------|------|--------|
| iso | string | ISO country from `GeoUtil.getCurrentCountryIso` |
| allowed | bool | from `call_record_states.xml` unless override ON |
**Effective allow** =
`CallRecorderService.isEnabled(context)` AND
(`extendrom_call_recording == 1` OR country map allows iso).
### CapturePolicy
| Field | Type | Rules |
|-------|------|--------|
| windowSecure | bool | app set `FLAG_SECURE` |
| override | bool | `disable_secure_windows == 1` |
**Effective block screenshot** = `windowSecure && !override` (for covered
system capture paths).
## Relationships
- Both Secure settings are owned by Settings Developer Options controllers.
- Dialer reads call-rec setting only in `canRecordInCurrentCountry()` (and any
equivalent callers).
- Framework/SystemUI reads FLAG_SECURE override on capture / secure checks.
+110
View File
@@ -0,0 +1,110 @@
# Implementation Plan: Call recording unlock + FLAG_SECURE override
**Branch**: `008-call-rec-flag-secure` | **Date**: 2026-09-16 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `/specs/008-call-rec-flag-secure/spec.md`
(roadmap `008` + `009`, global Developer Options toggles only).
## Summary
Ship two native Developer Options switches (same pattern as `005`
`allow_signature_fake`):
1. **Disable call recording restrictions** →
`Settings.Secure.extendrom_call_recording` (GOLDEN / EXTENDROM
`ER_ALLOW_ANY_CALL_REC`) — when ON, Dialer
`CallRecorder.canRecordInCurrentCountry()` allows recording regardless of
`call_record_states.xml` country flags.
2. **Allow screenshots of secure windows** →
`Settings.Secure.allow_flag_secure_override` — when ON, system capture
paths ignore `FLAG_SECURE` globally.
References: AXP GOLDEN Secure dump (`extendrom_call_recording=1`) +
EXTENDROM `er.sh` F_CALLREC / `overlays/call-rec` / `config/call-rec`.
FLAG_SECURE is project-native (not AXP catalog). Re-validate on LOS 23.2 /
A16. No Magisk / LSPosed / Zygisk at runtime. LAB only.
## Technical Context
**Language/Version**: Java (Dialer, Settings, frameworks/base); Secure settings;
optional Kotlin if Settings already uses it for DO controllers
**Primary Dependencies**: LOS 23.2 Dialer (`CallRecorder`,
`call_record_states.xml`); Settings Developer Options; SystemUI / WM capture
surfaces; EXTENDROM call-rec reference on
`/srv/rom/references/extendrom`
**Storage**: `Settings.Secure` keys; meta-repo patches under
`vendor/sunfish-a16/patches/008-…`
**Testing**: LAB flash/`adb`; `settings get/put`; call-record file evidence;
screenshot of secure window; Magisk-free image
**Target Platform**: Pixel 4a LAB `11231JEC207477`; Android 16 / LOS 23.2
**Project Type**: Android ROM (Dialer + Settings + frameworks)
**Performance Goals**: N/A (correctness)
**Constraints**: LAB only; default OFF; global only (no per-app); attributable
patches; no root modules; legal warning on call-rec UI
**Scale/Scope**: Two DO preferences; one Dialer gate; one (or few) capture /
`FLAG_SECURE` enforcement sites; docs + LAB evidence
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
| Gate | Status |
|------|--------|
| LAB only; never GOLDEN | pass |
| Native call recording; re-validate A16 (no blind AXP XML) | pass (Dialer gate located on LOS 23.2) |
| FLAG_SECURE: default stock; user global override; no LSPosed | pass (global only) |
| No Magisk as production stage | pass |
| Small attributable reversible patches | pass (EXTENDROM F_CALLREC / native FLAG_SECURE) |
| Do not pull banking / incomplete `007` into this feature | pass |
**Post-design**: unchanged — FLAG_SECURE is global-only (not planned per-app);
capture paths that stay DRM-blocked must be documented if found on LAB.
## Project Structure
### Documentation (this feature)
```text
specs/008-call-rec-flag-secure/
├── plan.md # this file
├── research.md # Phase 0
├── data-model.md # Phase 1
├── quickstart.md # Phase 1
├── contracts/
│ ├── call-recording-surface.md
│ └── flag-secure-surface.md
└── tasks.md # /speckit-tasks (not this command)
```
### Host + tree
```text
# Meta-repo
vendor/sunfish-a16/patches/008-call-rec-flag-secure/ # apply scripts (to add)
docs/008-009-design.md
# rom-dev01
/srv/rom/src/lineage/packages/apps/Dialer/
java/com/android/incallui/call/CallRecorder.java # canRecordInCurrentCountry()
java/com/android/dialer/callrecord/res/xml/call_record_states.xml
/srv/rom/src/lineage/packages/apps/Settings/ # DO controllers + strings
/srv/rom/src/lineage/frameworks/base/ # Secure settings + FLAG_SECURE
/srv/rom/references/extendrom/overlays/call-rec/
/srv/rom/references/extendrom/config/call-rec/
/srv/rom/artifacts/008-call-rec-flag-secure/ # LAB evidence
```
**Structure Decision**: Same as `005` — attributable patches applied on the
LOS tree via `vendor/sunfish-a16` scripts; meta-repo holds specs/docs/scripts,
not megapatches of Dialer/frameworks.
## Complexity Tracking
| Violation | Why needed | Simpler alternative rejected because |
|-----------|------------|--------------------------------------|
| Frameworks / SystemUI patches for FLAG_SECURE | Constitution requires native override | Magisk module forbidden in production |
| Dialer patch (not overlay-only) | Country gate is Java `canRecordInCurrentCountry()` | Blind A11 overlay alone does not wire the Secure setting |
## Phase 0 / Phase 1
See `research.md`, `data-model.md`, `contracts/*`, `quickstart.md`.
@@ -0,0 +1,69 @@
# Quickstart: `008` call-rec + FLAG_SECURE (LAB)
**Device**: LAB only `11231JEC207477`
**Build**: bacon/image with `008-call-rec-flag-secure` applied
**Evidence root**: `/srv/rom/artifacts/008-call-rec-flag-secure/`
## Prerequisites
- Magisk **not** required (must work without it).
- Developer Options enabled.
- Contracts: [call-recording-surface.md](./contracts/call-recording-surface.md),
[flag-secure-surface.md](./contracts/flag-secure-surface.md).
## 1. Defaults
```bash
adb -s 11231JEC207477 shell settings get secure extendrom_call_recording
adb -s 11231JEC207477 shell settings get secure disable_secure_windows
# expect 0 or null
```
## 2. Call recording (region unlock)
1. Open **Developer Options** → enable **Disable call recording restrictions**.
2. Confirm:
```bash
adb shell settings get secure extendrom_call_recording
# expect 1
```
3. Place or receive a call with Dialer → start recording → stop → confirm file
(Downloads / Dialer recordings — note path).
4. Disable the switch (or turn off Developer Options) → confirm setting `0`
and that a restricted country would hide/deny record (or document LAB
country ISO + XML flag).
Store notes + path under `…/call-rec/`.
## 3. FLAG_SECURE override
1. Enable **Allow screenshots of secure windows** in Developer Options.
2. Confirm:
```bash
adb shell settings get secure disable_secure_windows
# expect 1
```
3. Open an app/window that sets `FLAG_SECURE` (e.g. password manager, banking
login, or a tiny test APK).
4. With toggle **OFF**: system screenshot should fail / blank.
5. With toggle **ON**: screenshot or screen record should succeed.
6. Toggle OFF again → secure behaviour restored.
Store images/notes under `…/flag-secure/`.
## 4. Pass criteria
- [ ] Both settings default OFF after fresh boot / DO off
- [ ] Call-rec ON → recording works; OFF → stock policy
- [ ] FLAG_SECURE ON → capture works; OFF → blocked
- [ ] No Magisk installed for the successful run
## Notes
- User is responsible for call-recording legality.
- DRM-protected surfaces may still resist capture; note exceptions.
- `user` build retest before release claims (`011`).
@@ -0,0 +1,61 @@
# Research: `008-call-rec-flag-secure`
## Decision: Call-rec = AXP GOLDEN / EXTENDROM native model
**Decision**: Port EXTENDROM `F_CALLREC` (same behaviour as AXP GOLDEN), not
Magisk Dialer RRO.
**GOLDEN evidence**: LAB dump already has
`extendrom_call_recording=1` in
`.local-artifacts/002-golden/20260915-spoofing-phonesky/settings/secure-*.txt`.
**Mechanism** (`/srv/rom/references/extendrom/er.sh`):
1. Overlay Dialer `config.xml`: `call_recording_enabled=true`,
`call_recording_audio_source=4`.
2. Settings DO: category + legal info + switch
`extendrom_call_recording`.
3. Controllers: `ER_CallRecPreferenceController` /
`ER_CallRecInfo` → `Settings.Secure.ER_ALLOW_ANY_CALL_REC`.
4. `Settings.java`:
`ER_ALLOW_ANY_CALL_REC = "extendrom_call_recording"`.
5. Dialer `CallRecorder.canRecordInCurrentCountry()`: if Secure int == 1,
return `true` before country XML map.
**Secure key (product)**: `extendrom_call_recording` (GOLDEN-compatible).
Do **not** invent `allow_call_recording_any_region` for call-rec.
**Alternatives rejected**:
- Magisk static RRO of `call_record_states.xml` — always-on, not native ROM,
no DO parity with GOLDEN.
- Blind A11 overlay-only without Dialer Java gate — may no-op on A16.
## Decision: FLAG_SECURE = AOSP `disable_secure_windows` + DO
**Decision**: Do **not** invent a second frameworks hook. LOS 23.2 / A16 already
has `Settings.Secure.DISABLE_SECURE_WINDOWS` wired through
`WindowManagerService` → `WindowState.isSecureLocked()`. Ship a Developer
Options switch (preference key `allow_flag_secure_override`) that writes
`disable_secure_windows`.
**Constraint**: WMS applies the setting only on debuggable builds
(`ro.debuggable`). LAB `userdebug` is in scope; `user` retest before release.
**Alternatives rejected**: Magisk `services.jar` dex patchers as shipping
mechanism.
## Decision: Packaging = `vendor/sunfish-a16` apply like `005`
Idempotent script under `patches/008-call-rec-flag-secure/`, EXTENDROM/`er.sh`
attribution for call-rec.
## Decision: Combined feature directory
One Spec Kit feature absorbs roadmap `008` + `009`.
## Decision: No Magisk artifacts in the repo
Owner-supplied Magisk zips were temporary agent aids only; removed from the
working tree. Implementation references EXTENDROM + GOLDEN dumps + A16 source
inventory.
+170
View File
@@ -0,0 +1,170 @@
# Feature Specification: Call recording unlock + FLAG_SECURE override
**Feature Branch**: `008-call-rec-flag-secure`
**Created**: 2026-09-16
**Status**: Draft
**Input**: Deliver constitution items `008` and `009` together as native ROM
features with two Developer Options toggles (global only). Call recording =
disable region/country restrictions via **AXP GOLDEN / EXTENDROM native**
`extendrom_call_recording` DO gate. FLAG_SECURE = optional global allow
screenshots/capture of secure windows (native frameworks + DO). Pattern
matches `005` DO style. No LSPosed/Zygisk/Magisk runtime. LAB only.
**Design**: [`docs/008-009-design.md`](../../docs/008-009-design.md)
## Absorbs roadmap
| # | Theme | In this feature |
|---|--------|-----------------|
| 008 | Unrestricted call recording (region) | Yes |
| 009 | FLAG_SECURE override | Yes — **global only** |
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Call recording region unlock toggle (Priority: P1)
As a LAB/daily-driver user, I can enable **Disable call recording
restrictions** under Developer Options. When ON, the Dialer no longer blocks
recording solely because of country/region rules. When OFF, stock LOS
restrictions apply.
**Why this priority**: Core `008` value; small surface; matches EXTENDROM/AXP
intent without porting full AXP recording UX.
**Independent Test**: Toggle setting; `settings get secure
extendrom_call_recording`; attempt call recording on LAB Dialer with
toggle on vs off.
**Acceptance Scenarios**:
1. **Given** a build with this feature on LAB, **When** Developer Options is
opened, **Then** a switch for disabling call-recording region restrictions
is visible (with a short legal responsibility summary).
2. **Given** the switch is OFF (default), **When** Dialer evaluates recording
eligibility, **Then** stock country/region rules apply.
3. **Given** the switch is ON, **When** the user records a call with the
system Dialer, **Then** recording is not refused solely due to region /
country policy, and a recording artifact is produced.
4. **Given** Developer Options is disabled globally, **When** that happens,
**Then** `extendrom_call_recording` returns to `0`.
---
### User Story 2 - Global FLAG_SECURE / screenshot override (Priority: P1)
As a LAB/daily-driver user, I can enable **Allow screenshots of secure
windows** under Developer Options. When ON, screenshots and screen capture
work even if apps set `FLAG_SECURE`. When OFF, Android’s normal secure-window
behaviour applies. The override is **global** (all apps).
**Why this priority**: Constitution `009` project requirement; same DO pattern
as spoofing.
**Independent Test**: App or screen that sets `FLAG_SECURE`; capture with
toggle OFF (expect fail) and ON (expect success);
`settings get secure disable_secure_windows`.
**Acceptance Scenarios**:
1. **Given** a build with this feature on LAB, **When** Developer Options is
opened, **Then** a switch for secure-window screenshot override is visible.
2. **Given** the switch is OFF (default), **When** the user tries to screenshot
a `FLAG_SECURE` window, **Then** capture is blocked as on stock.
3. **Given** the switch is ON, **When** the user screenshots or screen-records
that same window, **Then** capture succeeds.
4. **Given** Developer Options is disabled globally, **When** that happens,
**Then** `disable_secure_windows` returns to `0`.
---
### User Story 3 - Native, attributable packaging (Priority: P2)
As a maintainer, both behaviours ship in the bacon/ROM tree via
`vendor/sunfish-a16` patches (idempotent apply), sourced from EXTENDROM /
AXP GOLDEN call-rec (and native FLAG_SECURE work), without Magisk modules.
**Why this priority**: Constitution: native ROM, attributable, reversible.
**Independent Test**: Patch script dry-run; tree markers; no Magisk zip in
artifacts required for the feature to work after flash.
**Acceptance Scenarios**:
1. **Given** a clean LOS tree after `repo sync`, **When** the apply script
runs, **Then** it is idempotent and records source attribution.
2. **Given** a flashed LAB image, **When** Magisk is not installed, **Then**
both toggles still function.
---
### Edge Cases
- Dialer path renamed on A16 → re-locate gate; do not assume A11 overlay paths.
- Call recording legal in some jurisdictions only → UI must warn; feature does
not claim legality.
- Some capture paths (DRM / hardware buffers) may still block — document if a
specific path remains blocked after toggle ON.
- `user` builds: follow same policy as `005` for whether DO toggles are
available (document decision in plan if it differs from `userdebug` LAB).
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: System MUST expose Developer Options switch **Disable call
recording restrictions** backed by
`Settings.Secure.extendrom_call_recording` ∈ {`0`,`1`}, default `0`.
- **FR-002**: When that setting is `1`, Dialer MUST NOT deny call recording
solely due to country/region restriction logic.
- **FR-003**: When that setting is `0`, Dialer MUST use stock restriction
behaviour.
- **FR-004**: System MUST expose Developer Options switch **Allow screenshots
of secure windows** backed by
`Settings.Secure.disable_secure_windows` (DO: `allow_flag_secure_override`) ∈ {`0`,`1`}, default `0`.
- **FR-005**: When that setting is `1`, system screenshot and screen-capture
paths MUST allow capture of windows that set `FLAG_SECURE` (global).
- **FR-006**: When that setting is `0`, `FLAG_SECURE` MUST behave as stock.
- **FR-007**: Both settings MUST reset to `0` when Developer Options is turned
off (same pattern as EXTENDROM / `005`).
- **FR-008**: Implementation MUST NOT require Magisk, LSPosed, or Zygisk at
runtime.
- **FR-009**: Patches MUST be attributable (EXTENDROM/`er.sh` F_CALLREC for
call-rec; native frameworks for FLAG_SECURE) and apply via
`vendor/sunfish-a16/patches/…`.
- **FR-010**: Validation MUST be LAB-only (`11231JEC207477`).
### Key Entities
- **SecureSetting**: `extendrom_call_recording`,
`disable_secure_windows`.
- **DeveloperOptionsController**: preference controllers writing those
settings.
- **DialerRecordingPolicy**: country/region gate gated by call-rec setting.
- **SecureCapturePolicy**: framework capture/`FLAG_SECURE` enforcement gated
by screenshot setting.
## Success Criteria *(mandatory)*
- **SC-001**: On LAB, both settings read `0` after fresh boot / DO off.
- **SC-002**: With call-rec toggle ON, operator completes a recorded call and
stores evidence under `/srv/rom/artifacts/008-…/call-rec/`.
- **SC-003**: With screenshot toggle ON, operator captures a previously secure
window; evidence under `/srv/rom/artifacts/008-…/flag-secure/`.
- **SC-004**: Toggles OFF restore stock deny behaviour for the same tests.
- **SC-005**: Feature works on a Magisk-free LAB image.
## Assumptions
- LOS 23.2 Dialer still has an identifiable country/region recording gate (or
equivalent CarrierConfig path) that can be overridden.
- “Screenshot” includes the system screenshot gesture and common screen-record
entry points; exotic DRM paths may remain blocked (document).
- FLAG_SECURE is **global only** (not planned: per-app lists).
- `007` remains incomplete; this feature does not depend on KernelSU.
## Out of scope
- AXP full call-recording automation UI.
- Per-app FLAG_SECURE lists.
- Relocked AVB / signed release (`011`).
- Banking PASS (`007`).
+226
View File
@@ -0,0 +1,226 @@
# Tasks: Call recording unlock + FLAG_SECURE override (`008`/`009`)
**Input**: Design docs from `/specs/008-call-rec-flag-secure/`
**Prerequisites**: `plan.md`, `spec.md`, `research.md`, `data-model.md`,
`contracts/`, `quickstart.md`; constitution native-ROM rules; **AXP GOLDEN /
EXTENDROM call-rec model** (not Magisk modules)
**Primary oracle (call-rec)**: AXP/EXTENDROM native stack — same Secure key
already observed on GOLDEN LAB dump
(`.local-artifacts/002-golden/…/settings/secure-*.txt` →
`extendrom_call_recording=1`):
| Piece | Host reference |
|-------|----------------|
| Apply logic | `/srv/rom/references/extendrom/er.sh` (`F_CALLREC` / unrestricted call recording) |
| DO controllers | `/srv/rom/references/extendrom/config/call-rec/…/CallRecPreferenceController.java` (+ Info) |
| Strings / Dialer config overlay | `/srv/rom/references/extendrom/overlays/call-rec/` |
| Docs | `/srv/rom/references/axp/archive/…/knowledge/callrec/` |
**FLAG_SECURE**: project-specific native DO (not AXP Pro catalog). Magisk
modules are **agent-only mental aids** if ever consulted — **never** ship,
install, or task-move zips into the tree.
**Organization**: US1 call-rec (GOLDEN model) → US2 FLAG_SECURE → US3 packaging.
**Out of scope**: Magisk/Zygisk runtime; static Dialer RRO product; per-app
FLAG_SECURE lists (not planned); full AXP auto-record policy UI; `007`; GOLDEN
flash; commit/MR.
## Format: `[ID] [P?] [Story] Description`
- **[P]**: Can run in parallel (different files, no incomplete deps)
- **[Story]**: US1…US3 from `spec.md`
## Path conventions
- Meta repo: `vendor/sunfish-a16/`, `docs/`, `specs/008-call-rec-flag-secure/`
- Build host: `/srv/rom/src/lineage/`, `/srv/rom/references/extendrom/`,
`/srv/rom/artifacts/008-call-rec-flag-secure/`
- GOLDEN settings sample (read-only): `.local-artifacts/002-golden/`
---
## Phase 1: Setup (Shared Infrastructure)
**Purpose**: Artifact dirs + patch scaffolding; **no Magisk zip handling**
- [x] T001 [P] Create host dirs `/srv/rom/artifacts/008-call-rec-flag-secure/`
(`call-rec/`, `flag-secure/`) and `/srv/rom/logs/008-build/`
(android-build writable)
- [x] T002 [P] Add stub `docs/008-call-rec-flag-secure-runbook.md` (GOLDEN/
EXTENDROM call-rec map, Secure keys, LAB serial, apply order)
- [x] T003 [P] Create `vendor/sunfish-a16/patches/008-call-rec-flag-secure/README.md`
(idempotent apply like `005-sigspoof`; attribute EXTENDROM/`er.sh` F_CALLREC;
FLAG_SECURE as native project patch)
**Checkpoint**: Dirs + stubs exist
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: Port map from GOLDEN/EXTENDROM onto LOS 23.2 paths; Secure key
plumbing; **no bacon yet**
**⚠️ CRITICAL**: Blocks all user stories
- [x] T004 Extract F_CALLREC steps from
`/srv/rom/references/extendrom/er.sh` into a checklist in
`docs/008-call-rec-flag-secure-runbook.md` (Settings controllers, Secure
`ER_ALLOW_ANY_CALL_REC` = `"extendrom_call_recording"`, DO XML, Dialer
`CallRecorder.canRecordInCurrentCountry` early-return, Dialer
`config.xml` overlay)
- [x] T005 [P] Confirm LOS 23.2 Dialer still uses
`/srv/rom/src/lineage/packages/apps/Dialer/java/com/android/incallui/call/CallRecorder.java`
and that stock `call_recording_enabled` / audio source match or need
EXTENDROM overlay (`call_recording_enabled=true`,
`call_recording_audio_source=4`)
- [x] T006 [P] Inventory A16 `FLAG_SECURE` / screenshot / screen-record sites
under `/srv/rom/src/lineage/frameworks/base/` (+ SystemUI); list candidates
in the runbook (native source patches only)
- [x] T007 [P] Locate Settings development dashboard +
`development_settings.xml` paths on LOS 23.2 (same family as `005`)
- [x] T008 Scaffold idempotent
`vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_flag_secure.py`
(dry-run + markers; body filled by story tasks)
**Checkpoint**: Foundation ready — US1/US2 can proceed in parallel
---
## Phase 3: User Story 1 — Call recording (GOLDEN / EXTENDROM model) (P1) 🎯 MVP
**Goal**: Native DO **Disable call recording restrictions** writing Secure
`extendrom_call_recording` (GOLDEN-compatible); Dialer allows any country when
`1`
**Independent Test**: `adb shell settings get secure extendrom_call_recording`;
toggle; record call per `quickstart.md` and
`contracts/call-recording-surface.md`
### Implementation for User Story 1
- [x] T009 [P] [US1] Port EXTENDROM strings + DO category/switch/info into
`/srv/rom/src/lineage/packages/apps/Settings/` (keys
`extendrom_call_recording*`, legal details text from EXTENDROM overlay)
- [x] T010 [P] [US1] Add `ER_CallRecPreferenceController` + `ER_CallRecInfo`
(or equivalent names) under Settings development; wire into
`DevelopmentSettingsDashboardFragment`; reset Secure to `0` when DO
master disabled
- [x] T011 [US1] Add
`Settings.Secure.ER_ALLOW_ANY_CALL_REC = "extendrom_call_recording"` in
`/srv/rom/src/lineage/frameworks/base/core/java/android/provider/Settings.java`
- [x] T012 [US1] Patch
`CallRecorder.canRecordInCurrentCountry()` to early-return `true` when
`Settings.Secure.getInt(…, "extendrom_call_recording", 0) == 1` (same
injection as `er.sh` F_CALLREC)
- [x] T013 [P] [US1] Ensure Dialer call-rec config enabled for sunfish
(EXTENDROM overlay values) under Dialer
`callrecord/res/values/config.xml` or product overlay
- [x] T014 [US1] Encode US1 changes into
`vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_flag_secure.py`
with EXTENDROM/`er.sh` attribution
- [ ] T015 [US1] LAB validate *(blocked: wait 008-bacon + Magisk-free flash)* on `11231JEC207477`; evidence under
`/srv/rom/artifacts/008-call-rec-flag-secure/call-rec/`
**Checkpoint**: US1 independently testable (MVP)
---
## Phase 4: User Story 2 — Global FLAG_SECURE override (P1)
**Goal**: DO **Allow screenshots of secure windows** → Secure
`allow_flag_secure_override`; native capture paths honour it globally
**Independent Test**: Secure window OFF fail / ON success;
`settings get secure allow_flag_secure_override`; `quickstart.md` §FLAG_SECURE
### Implementation for User Story 2
- [x] T016 [P] [US2] Settings strings + switch for FLAG_SECURE override under
`/srv/rom/src/lineage/packages/apps/Settings/`
- [x] T017 [P] [US2] Preference controller for
`Settings.Secure.allow_flag_secure_override` with DO-master reset to `0`
- [x] T018 [US2] Implement override in inventoried frameworks/SystemUI paths
(T006) — **source only**, no jar binary patchers
- [x] T019 [US2] Encode US2 into
`vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_flag_secure.py`
- [ ] T020 [US2] LAB validate *(blocked: wait 008-bacon + Magisk-free flash)*; evidence under
`/srv/rom/artifacts/008-call-rec-flag-secure/flag-secure/`; note any
remaining DRM blocks in the runbook
**Checkpoint**: US1 + US2 both work
---
## Phase 5: User Story 3 — Native attributable packaging (P2)
**Goal**: Idempotent `vendor/sunfish-a16` apply; Magisk-free image
**Independent Test**: Apply dry-run twice; feature works without Magisk
(SC-005)
- [x] T021 [US3] Finish apply script (idempotent, backups, clear missing-path
errors); README lists every touched path
- [ ] T022 [US3] Apply on `rom-dev01`, bacon detached *(apply done; bacon session `008-bacon` running)* via
`/srv/rom/artifacts/bin/rom-detach`, flash LAB **without** Magisk modules
for this feature
- [ ] T023 [US3] Record Magisk-free PASS *(blocked: post-flash)* under
`/srv/rom/artifacts/008-call-rec-flag-secure/` + runbook
**Checkpoint**: Packaging complete
---
## Phase 6: Polish & Cross-Cutting
- [x] T024 [P] Sync `docs/008-009-design.md`, `research.md`, contracts, and
GOLDEN inventory note (`extendrom_call_recording` on GOLDEN) with final
A16 paths
- [x] T025 [P] Verify DO master-off resets **both** Secure keys to `0`
- [ ] T026 Run full `quickstart.md` *(blocked: post-flash)* checklist; evidence index README under
`/srv/rom/artifacts/008-call-rec-flag-secure/`
- [x] T027 [P] Document `user` vs `userdebug` DO availability (same caution as
`005`)
---
## Dependencies & Execution Order
- **Setup → Foundational → US1 ∥ US2 → US3 → Polish**
- **US1** after Phase 2; no dep on US2
- **US2** after Phase 2; no dep on US1 (coordinate Settings XML)
- **US3** after US1+US2 encoded in apply script
### Parallel opportunities
- T001 ∥ T002 ∥ T003
- T004 then T005 ∥ T006 ∥ T007
- After Phase 2: US1 track (T009–T013) ∥ US2 track (T016–T018)
- T024 ∥ T025 ∥ T027
---
## Implementation Strategy
### MVP
1. Setup + Foundational (esp. T004/T005 from `er.sh`)
2. US1 GOLDEN call-rec → LAB evidence
3. Then US2 FLAG_SECURE
4. US3 packaging + Magisk-free proof
### Suggested MVP scope
**US1 only** (native EXTENDROM/GOLDEN call-rec). US2 still required before
calling roadmap `008`+`009` done.
---
## Notes
- Product is **native ROM** patches. Magisk zips are not part of the feature.
- Prefer Secure-setting Dialer gate (GOLDEN) over always-on country XML RRO.
- Prefer frameworks source patches over `services.jar` binary patching.
- LAB only: `11231JEC207477`. Owner handles commit/MR.
@@ -0,0 +1,43 @@
# Specification Quality Checklist: AXP LOS22 feature port to LOS23
**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-09-16
**Updated**: 2026-09-16 (owner clarifications applied)
**Feature**: [spec.md](../spec.md)
## Content Quality
- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed
## Requirement Completeness
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified
## Feature Readiness
- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification
## Notes
- Clarifications resolved: Magisk like GOLDEN (newest; AXP preinstall state);
full AXP resurrection including hardening/signing/AVB; WebView = LOS23
default with chooser unlocked (Aosmium EOL); Phonesky = project recent
patched artifact (reconsider if GOLDEN-class fixes missing); microG newest
+ integrated; spoofing and call recording selectable; KernelSU out of ship
for now with optional recycle of existing `007` functional kernels (FR-016).
- Named product components (microG, Phonesky, Magisk, Island) appear because
they are the resurrection surface, not implementation design.
- Ready for `/speckit-plan` (optional `/speckit-clarify` only if new gaps).
@@ -0,0 +1,53 @@
# Contract: LAB acceptance gates (`009`)
**Device**: LAB `11231JEC207477` only — never GOLDEN
**Evidence**: `/srv/rom/artifacts/009-axp-los23-port/<build-id>/`
## Gate A — Boot / hardware
- [ ] Clean flash boots to home
- [ ] Call + Wi‑Fi + unlock smoke (vanilla class)
## Gate B — Google stack
- [ ] microG Self-Check required items pass with spoofing ON as needed
- [ ] Project Phonesky opens; ordinary app install/update works
- [ ] No FakeStore primary
- [ ] Version pins: microG/GSF newest stable; Phonesky = project artifact
- [ ] If Play lacks GOLDEN-class fix → **FAIL closed** (reconsider Phonesky)
## Gate C — Toggles
- [ ] Spoofing OFF/ON behaviour per `toggles-surface.md`
- [ ] Call-rec OFF/ON behaviour per `toggles-surface.md`
## Gate D — Magisk
- [ ] Preinstall state matches measured GOLDEN/AXP class
- [ ] Activation documented and completed on LAB
- [ ] DenyList-class hides root from a test/banking package
## Gate E — Banking
- [ ] Island present; Swissquote PASS in managed profile workflow
- [ ] Yuh recorded; not worse than GOLDEN for packaging/spoofing reasons
- [ ] Banks do not receive root
## Gate F — Usability / WebView
- [ ] Bundled AXP categories present or documented impossible
- [ ] Versions newest workable (except Phonesky rule)
- [ ] WebView default LOS; chooser unlocked
## Gate G — Hardening / signing class
- [ ] GOLDEN-class hardening/signing/AVB posture present **or** each gap
documented as impossible on this stack (no silent omit)
- [ ] Staged known-good images retained for bisect
## Non-gates
- KernelSU presence or SusFS
- FLAG_SECURE override (optional)
- Custom AXP backup ROM feature
- Yuh PASS if GOLDEN attestation path still FAILs equivalently
@@ -0,0 +1,38 @@
# Contract: Magisk preinstall (AXP / GOLDEN class)
**Ship root**: Magisk (newest stable that works on this stack)
**Not ship**: KernelSU / SusFS (`007` optional recycle only)
## Behavioural contract
1. After a clean `009` flash, Magisk is **present** in an AXP-like
**preinstalled but not fully activated** state (exact files/UI measured
from GOLDEN — document in LAB runbook).
2. Owner can complete activation on LAB using documented steps.
3. DenyList-class configuration can hide root from banking packages before
first process start (GOLDEN workflow).
4. Magisk version in the version-pin record is newest workable stable, not an
accidental stale host copy.
## Banking interaction
Aurora install → do not open → DenyList (incl. processes) → Island clone →
first launch in Island. Banks MUST NOT receive root.
## Out of contract
- Shipping Magia branding specifically ( Magisk upstream UI OK if behaviour
matches).
- Requiring KernelSU coexistence on the same ship image.
## Verification
```bash
# After flash (pre-activation): document paths/props matching GOLDEN notes
adb shell ls -l /system_ext/bin/magisk 2>/dev/null || true
adb shell pm list packages | grep -iE 'magisk|magia'
# After activation: Magisk app reports version; DenyList configurable
```
Evidence under `/srv/rom/artifacts/009-axp-los23-port/magisk/`.
@@ -0,0 +1,38 @@
# Contract: system packages (AXP resurrection)
**Device**: LAB only
**Build**: `009` product with Google stack + bundled AXP usability set
## Required packages (minimum)
| Role | Package | Notes |
|------|---------|--------|
| microG | `com.google.android.gms` | Newest stable verified; system |
| GSF Proxy | `com.google.android.gsf` | Newest stable verified |
| Phonesky | `com.android.vending` | Project patched priv-app; host artifact |
| Island | `com.oasisfeng.island…` | GOLDEN tool; newest workable |
| Magisk UI / stubs | as measured on GOLDEN | Preinstall layout; not fully activated until setup |
FakeStore / conflicting Companion MUST NOT be the primary Play path.
## Bundled usability set
Ship GOLDEN/AXP categories at newest workable stables (Aurora, F-Droid/repos as
on GOLDEN, launcher/gallery-class, DNS/hosts helpers if in inventory). Exact
package list = version-pin record derived from `002` + acceptance notes.
## WebView
- Default provider = LOS23 WebView package.
- Aosmium MUST NOT be required.
- `cmd webviewupdate` / Developer options chooser MUST remain usable.
## Verification snippets
```bash
adb shell pm path com.google.android.gms
adb shell pm path com.google.android.gsf
adb shell pm path com.android.vending
adb shell cmd package list packages | grep -E 'island|aurora|fdroid|magisk|magia'
adb shell cmd webviewupdate current
```
@@ -0,0 +1,36 @@
# Contract: selectable toggles (spoofing + call recording)
**Surface**: Developer Options (AXP-like)
**Defaults**: OFF (`0`) unless a documented project default says otherwise
## Signature spoofing
| Item | Value |
|------|--------|
| Secure key | `allow_signature_fake` (GOLDEN/AXP class from `005`) |
| OFF | Spoofing has no effect even if prior grants exist |
| ON | Authorized packages (microG, Phonesky) may use restricted spoofing |
| Forbidden | Unrestricted platform spoofing for arbitrary apps |
```bash
adb shell settings get secure allow_signature_fake
# expect 0/null default; 1 when enabled
```
## Call recording unlock
| Item | Value |
|------|--------|
| Secure key | `extendrom_call_recording` |
| OFF | Stock LOS country/carrier-style Dialer policy |
| ON | Dialer `canRecordInCurrentCountry()` allows recording |
```bash
adb shell settings get secure extendrom_call_recording
# expect 0/null default; 1 when enabled
```
## Optional (not AXP hard gate)
`disable_secure_windows` / FLAG_SECURE override may ship if already wired
(`008`); absence does not fail AXP resurrection acceptance.
@@ -0,0 +1,16 @@
# Contract: WebView policy
| Rule | Requirement |
|------|-------------|
| Default | LineageOS 23 WebView |
| Aosmium | Not shipped / not required (EOL) |
| Chooser | Developer options → WebView implementation remains available |
| Future | User may install an alternate WebView and select it |
```bash
adb shell cmd webviewupdate current
# default should be LOS provider, not org.axpos.aosmium
adb shell cmd webviewupdate
# provider list / update path must not be locked to a single forced package
```
+106
View File
@@ -0,0 +1,106 @@
# Data model: `009-axp-los23-port`
## Entities
### VanillaBaseline
| Field | Type | Rules |
|-------|------|--------|
| source | string | `003` LOS 23.2 sunfish (or documented equivalent) |
| manifest_rev | string | recorded repo manifest revision |
| lab_smoke | enum | `pass` required before AXP integrate stages |
### AxpCapabilitySet
| Field | Type | Rules |
|-------|------|--------|
| category | string | e.g. google-stack, magisk, banking, call-rec, bundled-apps, hardening |
| golden_ref | string | pointer into `002` / AXP Pro row |
| status | enum | `required` \| `exception` \| `impossible` |
| notes | string | WebView=exception; others default required |
### VersionPinRecord
| Field | Type | Rules |
|-------|------|--------|
| component | string | microG, GSF, Magisk, Island, Aurora, … |
| version_name | string | human version |
| version_code | int/string | as applicable |
| upstream_ref | string | tag/commit/URL |
| policy | enum | `newest_stable` \| `project_phonesky` \| `los_default` |
| verified_on | date | LAB verification date |
**Phonesky row**: `policy=project_phonesky` — must match host artifact id, not
“newest Play on internet”.
### PhoneskyHostArtifact
| Field | Type | Rules |
|-------|------|--------|
| path | string | under `/srv/rom/prebuilts/phonesky/` (not Git) |
| package | string | `com.android.vending` |
| signature | string | project/patch pipeline identity |
| privapp | bool | must be true |
| golden_fix_gap | bool | if true → halt acceptance / reconsider |
### MagiskPreinstallState
| Field | Type | Rules |
|-------|------|--------|
| magisk_version | string | newest stable verified |
| layout | string | measured GOLDEN/AXP preinstalled-not-fully-activated |
| activated_on_lab | bool | true after owner completes setup |
| denylist | set | banking packages hidden from root |
**Transitions**: `flashed_preinstall` → `activated` → `denylist_configured`
→ `banking_ready`.
### SecureToggle
| Field | Type | Rules |
|-------|------|--------|
| key | string | `allow_signature_fake` or `extendrom_call_recording` |
| value | int | `0` default / `1` on |
| surface | string | Developer Options |
### ManagedProfileBanking
| Field | Type | Rules |
|-------|------|--------|
| tool | string | Island (`com.oasisfeng.island…`) |
| swissquote | enum | `pass` required |
| yuh | enum | `pass` \| `fail_attestation_like_golden` \| `fail_regression` |
| root_to_banks | bool | must be false |
### WebViewPolicy
| Field | Type | Rules |
|-------|------|--------|
| default_provider | string | LOS23 WebView |
| aosmium | bool | must be false (not required/shipped) |
| chooser_unlocked | bool | must be true |
### LabAcceptanceBuild
| Field | Type | Rules |
|-------|------|--------|
| artifact_dir | string | `/srv/rom/artifacts/009-axp-los23-port/…` |
| hashes | map | SHA-256 of flashables |
| version_pins | ref | VersionPinRecord set |
| gates | ref | contracts/acceptance-gates.md checklist |
### OptionalKsuRecycleAsset
| Field | Type | Rules |
|-------|------|--------|
| path | string | existing `007` kernel/artifact |
| used_for | string | optional debug/reference |
| ship | bool | always false unless future spec says otherwise |
## Relationships
- `LabAcceptanceBuild` includes one `VersionPinRecord` set + Magisk state +
toggle defaults + banking results + WebViewPolicy.
- `PhoneskyHostArtifact` feeds product packaging; never stored in Git.
- `AxpCapabilitySet` drives which bundled packages appear in pins.
- `OptionalKsuRecycleAsset` must not appear as required on acceptance gates.
+130
View File
@@ -0,0 +1,130 @@
# Implementation Plan: AXP LOS22 feature port to LOS23 (resurrection)
**Branch**: `009-axp-los23-port` | **Date**: 2026-09-16 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `/specs/009-axp-los23-port/spec.md`
## Summary
Resurrect **AXP.OS Pro on LOS22 / GOLDEN** as a single shippable LineageOS
23.2 / Android 16 `sunfish` ROM on **LAB**: vanilla `003` base + full AXP
capability set (Google stack, selectable spoofing, Island banking workflow,
selectable call-recording unlock, Magisk like GOLDEN, bundled usability apps,
GOLDEN-class hardening/signing/AVB posture).
**Explicit exceptions:** LOS23 WebView default (Aosmium EOL) with unlocked
provider chooser; Phonesky = project's recent patched artifact (reconsider if
GOLDEN-class fixes missing); KernelSU (`007`) out of ship for now with optional
recycle of existing functional kernels (FR-016).
**Approach:** reuse proven `004`/`005`/`008` runbooks and `vendor/sunfish-a16`
packaging where they already match; replace the KernelSU ship path with
AXP-style Magisk preinstall (newest Magisk); pin newest stable microG and
bundled apps; keep Phonesky on the existing host pipeline; measure Magisk
“preinstalled but not fully activated” behaviour on GOLDEN before freezing LAB
setup docs.
## Technical Context
**Language/Version**: Android product makefiles / Soong; Java/Kotlin Settings +
Dialer + frameworks patches; bash runbooks on `rom-dev01`
**Primary Dependencies**: LOS 23.2 sunfish tree; microG + GSF releases; project
patched Phonesky host artifacts; Magisk (newest stable that works on this
stack); Island; AXP/GOLDEN inventory (`002`); EXTENDROM call-rec reference;
existing `vendor/sunfish-a16` overlays/patches from `004`/`005`/`008`
**Storage**: Host `/srv/rom/prebuilts/`, `/srv/rom/secrets/`,
`/srv/rom/artifacts/009-axp-los23-port/`; meta-repo docs + `vendor/sunfish-a16`
scripts (no proprietary APKs in Git)
**Testing**: LAB flash/`adb`; microG Self-Check; Play install; Island +
Swissquote/Yuh; Magisk DenyList-class; DO toggles; version-pin record
**Target Platform**: Pixel 4a LAB `11231JEC207477`; Android 16 / LOS 23.2
**Project Type**: Android custom ROM (product overlay + attributable patches)
**Performance Goals**: N/A (daily-driver correctness / parity)
**Constraints**: LAB only; no GOLDEN flash; no Phonesky bytes in Git; Magisk
ship root (not KernelSU); newest stables except Phonesky artifact rule;
attributable reversible changes; long builds detached on `rom-dev01`
**Scale/Scope**: Full AXP resurrection surface in one feature (Google stack,
toggles, Magisk, bundled apps, hardening/signing class); **parallel track**
to the standby KernelSU / split roadmap (`004`–`008`/`007`), reusing their
artifacts without deleting that track
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
| Gate | Status |
|------|--------|
| LAB only; never GOLDEN | pass |
| Spec-as-source for active feature requirements | pass (`009` normative for ship) |
| Functionality first; AXP compatibility oracle | pass (resurrection goal) |
| No proprietary Google APKs / secrets in Git | pass (host paths) |
| Phonesky priv-app; no FakeStore primary | pass |
| Restricted selectable spoofing (not open platform) | pass (reuse `005` class) |
| Native call-rec unlock; re-validate A16 | pass (reuse `008` / EXTENDROM) |
| Prefer native over LSPosed/Zygisk modules for ROM features | pass |
| Small attributable reversible changes | pass (staged integrate; known-good images) |
| Base = LOS 23.2 sunfish stack (no unrelated prebuilt kernel) | pass |
| Constitution default root = KernelSU; no Magisk production stage | **variance** — see Complexity Tracking (owner ship decision in `009`) |
| Signing/AVB historically `011` | **variance** — GOLDEN-class signing/AVB posture pulled into `009` acceptance (may stage inside feature) |
**Post-design**: Magisk ship + signing/AVB-in-`009` remain justified variances;
KernelSU recycle stays optional (FR-016). Recommend a later constitution
amendment to align Root architecture text with Magisk resurrection — not
blocking this plan.
## Project Structure
### Documentation (this feature)
```text
specs/009-axp-los23-port/
├── plan.md # this file
├── research.md # Phase 0
├── data-model.md # Phase 1
├── quickstart.md # Phase 1
├── contracts/
│ ├── packages.md
│ ├── toggles-surface.md
│ ├── magisk-preinstall.md
│ ├── webview-surface.md
│ └── acceptance-gates.md
└── tasks.md # /speckit-tasks (not this command)
```
### Host + tree
```text
# Meta-repo
vendor/sunfish-a16/ # product mk, permissions, patch apply
docs/002-golden-inventory.md
docs/004-phonesky-runbook.md
docs/005-spoofing-runbook.md
docs/008-call-rec-flag-secure-runbook.md
docs/axp-pro-parity.md # non-normative; remap rows → 009 after tasks
# rom-dev01
/srv/rom/src/lineage/ # LOS 23.2 sunfish tree
/srv/rom/prebuilts/microg/ # newest microG + GSF
/srv/rom/prebuilts/phonesky/ # project patched Phonesky (+ XMLs)
/srv/rom/prebuilts/magisk/ # Magisk artifacts for AXP-like preinstall
/srv/rom/prebuilts/axp-apps/ # bundled usability apps (newest stables)
/srv/rom/secrets/ # Phonesky / later release keys (never Git)
/srv/rom/artifacts/009-axp-los23-port/ # images, hashes, version pins, LAB evidence
/srv/rom/artifacts/007-…/ # optional KSU kernels (recycle only)
```
**Structure Decision**: Same meta-repo pattern as `004`/`005`/`008` —
attributable packaging and patches via `vendor/sunfish-a16`; heavy tree and
proprietary blobs only on `rom-dev01`. No megapatch dumps in Git.
## Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|--------------------------------------|
| Magisk as ship root (constitution prefers KernelSU; forbids Magisk production stage) | Owner chose **AXP resurrection** = Magisk like GOLDEN (`009` FR-010); KSU out of ship for now | Keeping KernelSU abandons “todo igual que AXP”; Magisk A/B-only diagnostic does not meet ship parity |
| GOLDEN-class hardening/signing/AVB inside `009` (was split `011`–`013`) | Owner Q2 = full resurrection, not apps-only | Deferring signing/hardening recreates the decide-scatter `009` was meant to end |
| Single Magisk-resurrection feature owns large surface | Simplifies current ship without deleting standby KSU track | Re-merging both tracks into one ambiguous roadmap recreates decide-scatter |
## Phase 0 / Phase 1
See `research.md`, `data-model.md`, `contracts/*`, `quickstart.md`.
+81
View File
@@ -0,0 +1,81 @@
# Quickstart: `009` AXP resurrection (LAB validation)
**Device**: LAB only `11231JEC207477`
**Never**: flash / wipe / Magisk-change GOLDEN
**Evidence**: `/srv/rom/artifacts/009-axp-los23-port/<build-id>/`
**Contracts**: [packages](./contracts/packages.md),
[toggles](./contracts/toggles-surface.md),
[magisk](./contracts/magisk-preinstall.md),
[webview](./contracts/webview-surface.md),
[gates](./contracts/acceptance-gates.md)
## Prerequisites
- `003` vanilla (or documented equivalent) known-good on LAB at least once
- Host artifacts: newest microG/GSF, project Phonesky, Magisk prebuilts,
bundled app set — under `/srv/rom/prebuilts/` (not Git)
- GOLDEN Magisk preinstall notes measured (`adb` only) and copied into the
build’s magisk runbook snippet
- Prefer **clean flash** for acceptance (dirty upgrade from KSU LAB images is
best-effort only)
## 1. Flash + Gate A
1. Flash `009` images from artifact dir; record SHA-256.
2. Boot → unlock → place call → Wi‑Fi.
3. Save `getprop` + brief notes under `…/gate-a/`.
## 2. Google stack + toggles (Gates B/C)
1. Enable Developer Options.
2. Turn **Allow signature spoofing** ON; grant microG + Phonesky as required
(`005` runbook class).
3. microG Self-Check → screenshot.
4. Open Phonesky → install/update one ordinary app.
5. Confirm Secure keys:
```bash
adb -s 11231JEC207477 shell settings get secure allow_signature_fake
adb -s 11231JEC207477 shell settings get secure extendrom_call_recording
```
6. Call-rec: ON → record a call → OFF → note stock policy.
7. Record version pins (microG, GSF, Phonesky artifact id).
## 3. Magisk (Gate D)
1. Document pre-activation state vs GOLDEN notes.
2. Complete Magisk activation per runbook.
3. Configure DenyList-class hide for a banking package **before** first open.
4. Save Magisk version + screenshots under `…/magisk/`.
## 4. Banking (Gate E)
1. Aurora → install Swissquote → **do not open**.
2. DenyList → Island clone → first launch in Island → login.
3. Repeat attempt for Yuh; classify PASS / attestation-like-GOLDEN /
packaging regression.
4. Confirm banks lack root.
## 5. Usability + WebView (Gate F)
1. Confirm bundled categories vs pin record.
2. `adb shell cmd webviewupdate current` → LOS default; chooser still listed.
## 6. Hardening / signing class (Gate G)
1. Run the hardening/signing checklist defined for this build stage.
2. Any missing GOLDEN-class item → written impossible/gap note (no silent
skip).
## Pass criteria
All boxes in [acceptance-gates.md](./contracts/acceptance-gates.md) for the
claimed stage. Full resurrection acceptance = Gates A–G.
## Notes
- Optional `007` kernels: may flash for debug only; do not claim ship.
- Call-recording legality is the user’s responsibility.
- If project Phonesky misbehaves vs GOLDEN needs → stop and reconsider; do
not accept Gate B.
+130
View File
@@ -0,0 +1,130 @@
# Research: `009-axp-los23-port`
## Decision: Product = AXP resurrection on LOS 23.2 (parallel track)
**Decision**: Treat `009` as the normative ship feature **for the Magisk AXP
resurrection track** that ports the AXP.OS / GOLDEN LOS22 capability set onto
LineageOS 23.2 vanilla (`003`), with only the explicit exceptions in the spec
(WebView, Phonesky artifact, optional KSU recycle). Keep the prior KernelSU /
split roadmap (`004`–`008`/`007` + deferred items) on a **standby git branch**
without deleting or rewriting those specs.
**Rationale**: Owner wants Magisk “todo igual” to AXP now, without throwing
away the earlier KSU investment; either track may resume later.
**Alternatives considered**: Hard-supersede/delete the split roadmap —
rejected (owner wants standby). Continue only KSU track — rejected for
current ship preference.
## Decision: Magisk ship root (constitution variance)
**Decision**: Ship **Magisk** like GOLDEN — newest Magisk stable that works on
sunfish / LOS 23.2 — in an AXP-like **preinstalled but not fully activated**
layout. Measure the exact first-boot/setup UX on GOLDEN (`adb` only) before
freezing LAB instructions. DenyList-class hiding for banks.
**Rationale**: FR-010 / owner clarification; true AXP resurrection requires
Magisk/Magia-class root, not KernelSU.
**Alternatives considered**:
- KernelSU-Next (`007`) as ship — rejected for this feature (out of ship for
now).
- Rootless ship — rejected (banking DenyList workflow is part of GOLDEN).
- Magisk only as temporary A/B diagnostic — insufficient for resurrection
acceptance.
**Follow-up (non-blocking)**: Amend constitution Root architecture section in
a later docs pass so it no longer contradicts Magisk-as-ship.
## Decision: Optional recycle of `007` kernels (FR-016)
**Decision**: Keep the two functional KernelSU kernels/artifacts. Reuse only
if helpful (boot reference, differential debug, later optional path). Never
gate Magisk acceptance on KSU work.
**Alternatives considered**: Delete `007` artifacts — rejected (sunk cost;
optional value). Force dual-boot Magisk+KSU ship — rejected (complexity).
## Decision: Reuse `004`/`005` Google stack; bump microG; keep project Phonesky
**Decision**:
- microG GmsCore + GSF = **newest stable** (or newest tagged pre-release
upstream ships) verified on this stack; integrate with Phonesky as in AXP.
- Phonesky = **project patched artifact** from the recent host pipeline
(`docs/004-phonesky-runbook.md` / `/srv/rom/prebuilts/phonesky/`), not a
blind newest Play download.
- If LAB shows missing GOLDEN-class Play fixes with that artifact → **halt**
and reconsider Phonesky (spec edge case), do not silently ship FakeStore.
- Signature spoofing = selectable DO toggle (`allow_signature_fake` class from
`005`); restricted grants for microG + Phonesky only.
**Alternatives considered**: Newest Play APK every week without project patch
pipeline — rejected (breaks reproducible priv-app signing). FakeStore —
forbidden. Re-implement spoofing from scratch — reuse `005` unless A16 gaps.
## Decision: Selectable call recording = EXTENDROM / `008` model
**Decision**: Ship selectable call-recording unlock using GOLDEN-compatible
`Settings.Secure.extendrom_call_recording` + Dialer gate (EXTENDROM
`F_CALLREC` / `008` research). Default OFF.
**Alternatives considered**: Magisk Dialer RRO — rejected (not selectable /
not native). Blind old XML overlays — rejected for A16.
**Note**: Project `FLAG_SECURE` override remains optional vs AXP acceptance;
may ship if `008` work is cheap to include.
## Decision: WebView = LOS23 default, chooser unlocked
**Decision**: Do not ship Aosmium (EOL). Default = LineageOS 23 WebView.
Preserve Android multi-provider selection (Developer options → WebView
implementation).
**Alternatives considered**: Bundle Vanadium/AOSmium fork — rejected (EOL /
out of scope). Lock single provider — rejected (FR-015).
## Decision: Bundled apps = GOLDEN/AXP categories @ newest stable
**Decision**: Enumerate categories from `002` + AXP Pro catalog (Island,
Aurora, F-Droid/repos as on GOLDEN, launcher/gallery-class, DNS/hosts privacy
helpers if present). Install newest workable stables; pin versions in the
acceptance record. Island remains the managed-profile tool (measured on
GOLDEN).
**Alternatives considered**: LOS defaults only — fails resurrection. Exact
GOLDEN version freeze — rejected (owner wants newest stables except
Phonesky).
## Decision: Hardening / signing / AVB inside `009` (staged)
**Decision**: Acceptance includes GOLDEN-class hardening and signing/AVB
posture **as applicable** on LOS 23.2 / sunfish. Implement in **stages inside
`009`** (functional Magisk+Google+apps first, then hardening/signing gates),
but do not open a separate competing feature number for ship ownership.
Document impossible-on-sunfish gaps rather than silent omit.
**Rationale**: Owner Q2 = full resurrection; still obey constitution
“small attributable reversible changes” via staged known-good LAB images.
**Alternatives considered**: Defer all of `011`–`013` — rejected by owner.
One mega-commit everything — rejected (attribution / bisect).
## Decision: Banking workflow = GOLDEN Magisk DenyList + Island
**Decision**: Restore constitution banking steps with **Magisk DenyList**
(not KSU profiles) + Island: Aurora install → do not open → DenyList → clone
to Island → first launch in Island. Swissquote PASS hard gate; Yuh honest vs
GOLDEN attestation FAIL.
**Alternatives considered**: Rootless-only banking gate — incomplete vs AXP.
Insular rename — GOLDEN runs Island.
## Decision: Version freshness policy
**Decision**: Before long builds: `git fetch --tags` / refresh upstreams for
microG, Magisk, bundled apps; record tag/commit + version codes; never ship
from shallow or stale laptop caches (AGENTS.md lesson from `007`).
**Alternatives considered**: Trust whatever is already on the host — rejected.
+426
View File
@@ -0,0 +1,426 @@
# Feature Specification: AXP LOS22 feature port to LOS23
**Feature Branch**: `009-axp-los23-port`
**Created**: 2026-09-16
**Status**: Clarified — ready for `/speckit-plan`
**Input**: User description: "009-axp based LOS22 feature port to LOS23 —
take vanilla LineageOS 23 for sunfish and integrate the full AXP.OS (LOS22 /
Android 15 GOLDEN) feature set into LOS23 without product exceptions; only
versions change (newest stable for each system / bundled app that still fits
sunfish / Android 16); use the project's already updated patched Phonesky
(Google Play)."
**Owner clarifications (2026-09-16):** Magisk like GOLDEN (newest Magisk;
AXP-style preinstalled-but-not-fully-activated layout — measure on GOLDEN);
hardening / signing / AVB like GOLDEN (full **AXP resurrection** on LOS23);
WebView exception = keep LOS23 WebView (Aosmium EOL) but leave provider
selection unlocked; Phonesky = project patched build from recent work unless
it lacks GOLDEN-class fixes (then reconsider); microG newest stable integrated
with that Phonesky; signature spoofing and call recording **selectable**
(toggles); KernelSU (`007`) out of ship for now, with optional recycle of
existing functional kernels if useful; otherwise everything equal to
AXP/GOLDEN.
## Context / non-goals
**Product intent (this feature is normative for the AXP resurrection ship):**
- This feature is an **AXP.OS resurrection** on LineageOS 23.2 / Android 16
`sunfish`: same capability set as AXP on LOS22 / GOLDEN, moved to LOS23.
- Base ROM = LineageOS 23.2 / Android 16 `sunfish` vanilla from `003` (or the
best known-good vanilla-equivalent flashable baseline already proven on LAB).
- Feature source of truth for *what* to ship = what AXP.OS Pro / GOLDEN
provides on LOS22 (inventory in `002`, product catalog in
`docs/axp-pro-parity.md`), **without omitting AXP capabilities as “decide
later” rows**, including Magisk-class root and AXP-class hardening /
signing / AVB posture that GOLDEN represents.
- Versions = **newest stable** (or newest tagged pre-release the upstream
project currently ships) for each system/bundled component, constrained to
what still works on sunfish / non-GKI 4.14 / LOS 23.2 — not stale laptop
caches or shallow-clone version lies.
- **Phonesky exception:** ship the project's **already patched / fixed
Phonesky** from the recent host pipeline (not “whatever newest Play APK
is today”). If that build is missing fixes that GOLDEN's Phonesky path
still needs for daily-driver parity, stop and reconsider the Phonesky
plan before accepting the feature.
- **WebView exception:** do **not** ship Aosmium (EOL). Default WebView =
LineageOS 23's WebView. The ROM MUST keep Android's multi-provider path so
the owner can install another WebView later and select it (Developer
options → WebView implementation) — not locked to a single provider.
- Flash / experiment **LAB only**; GOLDEN remains the read-only oracle
(`docs/device-roles.md`).
**Relationship to earlier features:**
- `001`–`003` remain shared foundation (host, GOLDEN capture, vanilla build).
- **Two parallel product tracks** (neither deletes the other):
- **Standby / prior track:** `004`–`008` + `007` KernelSU (+ deferred
`006`/`010`–`013` as previously planned). Kept intact for later resume;
live on its own git branch once pushed (see plan notes).
- **Active Magisk-resurrection track (this feature):** AXP→LOS23 ship with
Magisk like GOLDEN. Does **not** rewrite or remove `004`–`008`/`007`
specs; it is a new Speckit directory that may **reuse** proven LAB
runbooks/results from those features when helpful.
- KernelSU / SusFS from `007` stays **out of this track’s ship** for now.
Recycle of the two functional `007` kernels is optional (FR-016) — never an
acceptance gate for Magisk.
- Project-only `FLAG_SECURE` global override may still ship if already
specified under `008` (not AXP catalog); it is optional relative to AXP
resurrection acceptance. AXP call-recording unlock **is** required and must
be selectable.
**Out of scope for this feature (explicit):**
- Flashing or modifying GOLDEN.
- Committing proprietary Google Phonesky APK bytes, private keys, or personal
identifiers to Git.
- Replacing the LOS 23.2 sunfish kernel/device stack with an unrelated prebuilt
(Graphene A13, stock A13, etc.).
- Shipping Aosmium / AOSmium as the default or only WebView (EOL).
- Building a custom AXP-style backup/restore ROM feature (personal backup
remains an external user tool if needed).
- Claiming Yuh hardware-attestation PASS if GOLDEN itself currently FAILs that
path — reproduce GOLDEN's *workflow* and document attestation parity honestly.
- Deleting or rewriting the standby KernelSU / split-roadmap specs
(`004`–`008`/`007`) — they remain valid on the standby branch/track.
- Shipping KernelSU-Next as this track’s root (out of ship here; Magisk is
the resurrection root). Optional later reuse of `007` kernels is allowed
under FR-016 but MUST NOT delay Magisk acceptance.
## User Scenarios & Testing *(mandatory)*
### User Story 1 - AXP resurrection daily-driver on LAB (Priority: P1)
As the owner, after flashing one LAB build produced under this feature, the
phone boots and presents the same class of daily-driver capabilities I rely on
with AXP on GOLDEN (Google compatibility stack, Play, managed banking profile
workflow, selectable call recording unlock, Magisk root/hide workflow like
GOLDEN, AXP hardening/signing posture, and AXP bundled usability apps —
except WebView as defined here), without a checklist of deferred AXP features.
**Why this priority**: This is the whole reason for the resurrection.
**Independent Test**: Flash one `009` build to LAB; walk the GOLDEN daily
checklist (microG Self-Check, Play open/install, Island profile, call-record
toggle off/on, spoofing toggle off/on, Magisk present in AXP-like install
state, bundled apps present, hardening/signing checks that GOLDEN implies);
compare against `002` inventory categories.
**Acceptance Scenarios**:
1. **Given** LAB on a vanilla `003` baseline (or documented equivalent),
**When** the `009` image is flashed, **Then** the device boots to home and
cellular/Wi‑Fi/basic hardware still work as in the vanilla smoke.
2. **Given** that build, **When** I compare shipped capability categories to
GOLDEN/AXP Pro (Google stack, Play, selectable spoofing, managed profile
tool, selectable call recording unlock, Magisk/DenyList-class path,
DNS/hosts privacy helpers if present on GOLDEN, F‑Droid/Aurora/launcher/
gallery-class bundled apps, hardening/signing/AVB class), **Then** no
category is left as “decide / later” without an explicit obsolescence note
tied to Android 16 / sunfish impossibility (WebView exception allowed per
FR-015).
3. **Given** Git and host artifact policy, **When** inspected, **Then** no
proprietary Phonesky APK bytes or secrets were committed.
---
### User Story 2 - Google stack: newest microG + project Phonesky (Priority: P1)
As the owner, newest-stable microG + GSF Proxy integrate with the project's
recent patched Phonesky so I can open Play, sign in as needed, and
install/update apps the same way I do on AXP; signature spoofing is
selectable (off = stock-safe default behaviour; on = needed Play/microG
path).
**Why this priority**: Without Play/microG parity, the ROM is not a daily
driver.
**Independent Test**: microG Self-Check; Phonesky launches; install or update
at least one ordinary app; toggle spoofing off/on and confirm expected
behaviour; push notifications smoke for a known test app.
**Acceptance Scenarios**:
1. **Given** the `009` build on LAB, **When** microG Settings Self-Check runs
with spoofing enabled as required, **Then** checks required for basic
Google compatibility pass (or each remaining failure is documented as
attestation/hardware outside packaging).
2. **Given** the project's current patched Phonesky host artifacts, **When**
the ROM is built and flashed, **Then** `com.android.vending` is present as
privileged Play and opens on LAB.
3. **Given** selectable signature spoofing, **When** the owner turns it on,
**Then** Play install/update works with microG without FakeStore; **When**
turned off, **Then** the device does not expose unrestricted spoofing to
arbitrary apps.
4. **Given** version policy, **When** microG / GSF versions are recorded,
**Then** each is the newest stable (or tagged pre-release upstream
currently ships) verified on this stack; Phonesky version matches the
project patched artifact (not a blind “newest Play from the internet”).
5. **Given** Play behaviour on LAB, **When** compared to GOLDEN needs, **Then**
if the project Phonesky lacks a GOLDEN-class fix required for daily use,
acceptance stops and the Phonesky plan is reconsidered.
---
### User Story 3 - Banking profile workflow parity (Priority: P1)
As the owner, I can recreate the GOLDEN banking layout on LAB: install via the
same store path I use on AXP, place banks in Island, and keep banking apps
free of root via Magisk DenyList-class hiding like GOLDEN.
**Why this priority**: Compatibility hard gates; daily-driver without banks
fails the resurrection.
**Independent Test**: Install Swissquote (hard gate) and attempt Yuh; run
inside Island; Magisk DenyList (or AXP-equivalent) applied; record PASS/FAIL
honestly vs GOLDEN.
**Acceptance Scenarios**:
1. **Given** Island on LAB, **When** I follow the documented GOLDEN banking
install order, **Then** Swissquote reaches a usable logged-in state in the
managed profile.
2. **Given** Yuh on the same workflow, **When** tested, **Then** either it
PASSes or the failure matches the known GOLDEN attestation-class failure
and is documented — not a silent packaging regression.
3. **Given** Magisk is present on the build, **When** banking apps run with
DenyList-class configuration, **Then** they do not receive root.
---
### User Story 4 - Magisk like GOLDEN + selectable call recording (Priority: P1)
As the owner, Magisk is present the way AXP/GOLDEN presents it (newest Magisk;
preinstalled but not fully activated until I finish setup — exact UX measured
from GOLDEN), with DenyList-class hiding; call recording unlock is selectable
like other Developer Options toggles.
**Why this priority**: Root and call recording are core AXP daily behaviours.
**Independent Test**: Confirm Magisk package/binary layout vs GOLDEN notes;
complete activation on LAB if required; DenyList a test app; toggle call
recording unlock off (restricted) and on (record allowed).
**Acceptance Scenarios**:
1. **Given** a fresh `009` flash, **When** inspected, **Then** Magisk is
present in an AXP-like preinstalled / not-fully-activated state consistent
with GOLDEN measurement, using the newest Magisk stable that works on this
stack.
2. **Given** Magisk setup completed on LAB, **When** DenyList-class hiding is
configured for a banking package, **Then** that package does not receive
root.
3. **Given** selectable call recording unlock, **When** OFF, **Then** stock
LOS country/carrier style restrictions apply; **When** ON, **Then** Dialer
can record on LAB without that block.
---
### User Story 5 - AXP usability, privacy, hardening (Priority: P2)
As the owner, the ROM ships the rest of the AXP usability/privacy surface and
GOLDEN-class hardening/signing/AVB posture at newest workable stables, with
LOS23 WebView default and an unlocked provider chooser.
**Why this priority**: Completes resurrection beyond Google/banking/root core.
**Independent Test**: Package list vs `002`; open bundled apps; confirm
default WebView is LOS and another WebView can be selected if installed;
hardening/signing checks agreed for GOLDEN-class parity.
**Acceptance Scenarios**:
1. **Given** the `009` build, **When** I list bundled system/user apps in the
AXP usability set, **Then** each GOLDEN/AXP category is present or
explicitly marked impossible on Android 16/sunfish with rationale
(Aosmium absence is the allowed WebView exception).
2. **Given** Developer options, **When** I open WebView implementation,
**Then** LOS23 WebView is available/default and the chooser is not locked
against future alternate providers.
3. **Given** version policy, **When** bundled apps (except Phonesky per
FR-004) are recorded, **Then** each is newest stable that still runs on
the ROM.
4. **Given** GOLDEN/AXP hardening and signing expectations, **When** the
accepted build is evaluated, **Then** equivalent class of protections /
signing posture is present or each gap is documented as impossible on
this stack — not silently dropped.
---
### User Story 6 - Parallel tracks stay honest (Priority: P2)
As the owner, docs make clear that **this** Speckit is the Magisk AXP
resurrection track on LOS23, while the prior KernelSU / split roadmap remains
on standby (other branch / same `004`–`008`/`007` specs untouched).
**Why this priority**: Lets either track resume without rewriting history.
**Independent Test**: `specs/004`–`008`/`007` still exist unchanged in intent;
`009` describes Magisk resurrection; git branches separate standby vs active
work when published.
**Acceptance Scenarios**:
1. **Given** the repo, **When** inspected, **Then** `001`–`008`/`007` spec
directories remain present (not deleted by `009`).
2. **Given** `009`, **When** read as ship intent for Magisk resurrection,
**Then** it does not claim to permanently retire the standby track — only
that KernelSU is out of *this* track’s ship for now.
---
### Edge Cases
- Newer “stable” of a bundled app breaks Android 16 / sunfish: pin last known
good, document the regression, and retry when a fixed release exists.
- Project Phonesky missing a GOLDEN-class fix: halt acceptance; reconsider
Phonesky patch/version plan before claiming Play parity.
- Magisk “preinstalled but not fully installed” UX differs across Magisk
versions: match GOLDEN's *class* of first-boot/setup behaviour; document
exact measured steps from GOLDEN before freezing LAB instructions.
- GOLDEN Yuh FAIL vs LAB FAIL: distinguish attestation/hardware class from
packaging/spoofing regressions.
- Vanilla LOS already provides an AXP capability (e.g. WireGuard, encryption):
keep LOS behaviour; do not duplicate.
- Dirty upgrade from older LAB experimental (incl. KernelSU) images: prefer
clean flash for acceptance.
- Phonesky host artifacts missing or wrong signature: fail closed — never
silently fall back to FakeStore.
- Alternate WebView installed by user later: selection must remain possible;
default remains LOS23 WebView until the user changes it.
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: The project MUST produce a flashable LineageOS 23.2 / Android 16
`sunfish` build that resurrects the AXP.OS on LOS22 / GOLDEN capability set
(including Magisk-class root and GOLDEN-class hardening / signing / AVB
posture), without leaving AXP capabilities as open “decide” omissions
except the WebView exception in FR-015.
- **FR-002**: Version selection for system and bundled components (except
Phonesky per FR-004) MUST prefer the newest stable (or newest tagged
pre-release upstream currently ships) that still works on sunfish /
LOS 23.2; GOLDEN versions are a floor reference, not a freeze.
- **FR-003**: The ROM MUST ship newest-stable microG Services Core and GSF
Proxy integrated with the project Phonesky (no FakeStore as primary Play).
- **FR-004**: The ROM MUST ship the project's recent patched Phonesky as
privileged `com.android.vending`, with a documented host-side obtain /
patch / sign / deploy path that never commits APK bytes to Git. If that
artifact lacks GOLDEN-class fixes required for daily-driver Play behaviour,
the Phonesky plan MUST be reconsidered before acceptance.
- **FR-005**: The ROM MUST provide **selectable** signature spoofing for the
microG + Phonesky path (owner-controlled toggle; default safe/off or
project-documented default). Spoofing MUST remain restricted/controlled —
not unrestricted platform spoofing for all apps.
- **FR-006**: The ROM MUST support the GOLDEN managed-profile banking workflow
(Island as measured on GOLDEN) including Swissquote PASS as a hard gate.
- **FR-007**: Banking applications MUST run without being granted root
(Magisk DenyList-class hiding).
- **FR-008**: The ROM MUST include **selectable** AXP-equivalent call
recording unlock (toggle; OFF = stock LOS restrictions, ON = unlock).
- **FR-009**: The ROM MUST include the AXP usability/privacy bundled surface
present on GOLDEN / AXP Pro (stores, launcher/gallery-class apps, DNS/hosts
or equivalent blockers when part of that surface) at newest workable
stables, subject to FR-015 for WebView.
- **FR-010**: The ROM MUST ship **Magisk** like GOLDEN: newest Magisk stable
that works on this stack, in an AXP-like preinstalled but not fully
activated layout (exact first-boot/setup behaviour measured from GOLDEN),
with DenyList-class hiding sufficient for FR-007. KernelSU is **out of
ship for now** (not the resurrection root path).
- **FR-011**: The feature acceptance gate MUST include GOLDEN/AXP-class
hardening and signing / AVB posture (as applicable on LOS 23.2 / sunfish),
not only apps and Google compatibility. Gaps MUST be documented as
impossible on this stack rather than silently omitted.
- **FR-012**: Acceptance flashing MUST use LAB only; GOLDEN MUST remain
unmodified by this feature's work.
- **FR-013**: Each shipped capability MUST be attributable (what upstream or
AXP behaviour it reproduces) and reversible (known-good prior LAB image
retained).
- **FR-014**: This feature MUST live as a **new Speckit** (`009`) in parallel
with the standby KernelSU / split roadmap (`004`–`008`/`007`). Those older
specs MUST NOT be deleted or rewritten to match Magisk resurrection.
Reusable runbooks/results from them MAY be referenced. For *this* track’s
ship only, Magisk (not KernelSU) is the root path.
- **FR-015**: Default WebView MUST be LineageOS 23's WebView. Aosmium MUST NOT
be required. The ROM MUST keep WebView provider selection available so a
future alternate WebView can be installed and chosen.
- **FR-016**: Existing functional KernelSU / related kernels and artifacts
from `007` SHOULD be retained and MAY be recycled when useful (reference
boot images, differential debugging, or a later optional non-ship path).
Such reuse is **optional** (“si se puede, ok”), MUST NOT become an
acceptance requirement for Magisk resurrection, and MUST NOT imply KernelSU
is back in the ship path unless a future spec explicitly says so.
### Key Entities
- **Vanilla baseline**: The LOS 23.2 sunfish build from `003` (or documented
equivalent) before AXP feature integration.
- **AXP capability set**: Inventory of features/packages/behaviours from
GOLDEN (`002`) plus AXP Pro catalog rows promoted by this spec
(resurrection target).
- **Version pin record**: Documented package/component versions chosen for the
accepted `009` build (newest workable stable each; Phonesky = project
artifact id/version).
- **Phonesky host artifact**: Project patched/signed Play APK and permission
metadata kept outside Git on the build host.
- **Magisk preinstall state**: Measured GOLDEN/AXP layout for Magisk present
but not fully activated, plus LAB setup steps.
- **LAB acceptance build**: Flashable image set + hashes + smoke/banking /
Magisk / hardening results for this feature.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: One LAB flash of a `009` build boots and completes the vanilla
hardware smoke within the same session as flash (owner can place a call,
use Wi‑Fi, and unlock the device).
- **SC-002**: 100% of AXP resurrection capability categories from the agreed
inventory are either demonstrated on LAB or explicitly documented as
impossible on Android 16/sunfish — zero silent omissions (WebView exception
allowed only as FR-015).
- **SC-003**: With spoofing enabled as required, microG Self-Check required
items and project Phonesky open/install succeed on LAB for ordinary apps on
the first accepted build candidate (or within one documented fix iteration).
- **SC-004**: Swissquote managed-profile + Magisk DenyList-class workflow
PASSes on LAB; Yuh result is recorded and is not worse than GOLDEN for
packaging/spoofing reasons.
- **SC-005**: Every bundled/system app version in the acceptance record
(except Phonesky) is newer than or equal to GOLDEN's measured version,
unless a newer release was tried and failed with a written regression note;
Phonesky matches the recorded project patched artifact.
- **SC-006**: Owner can turn signature spoofing and call recording unlock off
and on and observe the documented behaviour each time.
- **SC-007**: Magisk is present post-flash in the AXP-like preinstall state and
can be fully activated on LAB using documented steps measured from GOLDEN.
- **SC-008**: Default WebView is LOS23's; provider selection remains available.
- **SC-009**: Owner can perform the GOLDEN-equivalent daily checklist on LAB
without consulting a scatter of conflicting feature specs for what “ships”.
## Assumptions
- `003` vanilla LOS 23.2 sunfish is (or becomes) available as the integration
base; LAB stock rescue from `002` remains valid.
- GOLDEN inventory (`002`) plus AXP Pro docs are sufficient to enumerate the
capability set; live `adb` re-query of GOLDEN is allowed (especially Magisk
preinstall/activation details).
- “AXP resurrection” means Magisk + GOLDEN-class hardening/signing/AVB + full
AXP user surface on LOS23, with only the stated WebView and Phonesky-artifact
exceptions.
- Island remains the managed-profile tool because that is what GOLDEN
actually runs (AXP docs saying “Insular” are historical naming).
- FakeStore is never the primary Play path.
- Selectable spoofing and selectable call recording follow the same class of
Developer Options UX already explored in `005`/`008` runbooks where useful.
- Project `FLAG_SECURE` override is nice-to-have, not an AXP resurrection
hard gate.
- Personal app/data backup is not a ROM feature gate.
- Builds and long compiles run on `rom-dev01`; laptop holds docs/specs and
flash tools only.
- Prior KernelSU LAB experiments (including the two functional kernels already
built) are **out of ship for now** but kept as recyclable assets: reuse if
helpful, never required for Magisk acceptance.
+65
View File
@@ -0,0 +1,65 @@
# Feature Specifications
Numbered feature specs for `sunfish-a16-rom`.
## Structure
```text
specs/
└── NNN-short-name/
├── spec.md # Only human-maintained requirements source
├── plan.md # Derived — regenerate after spec changes
├── tasks.md # Derived — regenerate after spec changes
└── …
```
## Numbering
- Three-digit, sequential: `001-…`, `002-…`
- Short name: lowercase, hyphen-separated
- Create a feature directory only when ready to specify/implement it
## Spec-as-source
1. Edit requirements only in `spec.md`.
2. Run `/speckit-plan` and `/speckit-tasks` to regenerate derived artifacts.
3. Implementation follows the active feature; do not invent decisions missing
from the constitution or the active `spec.md`.
## Active / next features
| Dir | Intent |
|-----|--------|
| `001-development-environment` | ROM build host env on existing `rom-dev01` (not VM creation) |
| `002-axp-reference-capture` | Document GOLDEN AXP oracle + LAB stock rescue; no NeoBackup/wipe of GOLDEN |
| `003-lineage-23-vanilla-build` | First vanilla LOS 23.2 on **LAB only** → flash → boot → P1 (**done** 2026-09-14) |
| `004-microg-phonesky-integration` | microG + GSF Proxy + patched Phonesky on LAB (packaging done; Self-Check/auth → `005`) |
| `005-restricted-signature-spoofing` | Restricted spoofing + bacon zip — **done enough for LAB**; Location UI follow-up open |
| `007-kernelsu-sunfish` | ReSukiSU + SusFS bring-up — **incomplete** (banks FAIL; see `docs/007-lab-results-20260916.md`) |
| `008-call-rec-flag-secure` | Native DO toggles: call-rec region unlock + global FLAG_SECURE override — **next** |
Handoff snapshot: `docs/session-20260916-handoff.md`.
Design: `docs/008-009-design.md`.
## Roadmap (non-binding)
Later features are created only when prior work supplies enough information.
Numbers may shift; this list is guidance, not stubs:
| # | Theme |
|---|--------|
| 004 | microG + GSF Proxy + patched Phonesky |
| 005 | Restricted native signature spoofing (bacon shipped; **Location UI gap** follow-up) |
| 006 | **Rootless** banking baseline — **deferred** (absorbed into `007` gate) |
| 007 | ReSukiSU + SusFS — **incomplete** (banking hard-gate FAIL) |
| 008+009 | Delivered together as `008-call-rec-flag-secure` (global DO toggles only) |
| 010 | Remaining AXP Pro usability/privacy features (matrix spans 004–013; NeoBackup is external) |
| 011 | Signing / AVB / OTA + banking regression on signed/relocked ROM |
| 012 | Userspace hardening (promote from `docs/hardening-backlog.md`) |
| 013 | Kernel hardening + EOL firmware limits documentation |
## New feature
```text
/speckit-specify
```