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

+56
View File
@@ -0,0 +1,56 @@
# Specification Analysis Report — 001-development-environment (re-run)
**Date**: 2026-08-09
**Artifacts**: spec.md, plan.md (I1/I2 synced), tasks.md (T001–T048)
**Constitution**: 0.1.5
**Mode**: Read-only analysis
## Findings
| ID | Category | Severity | Location(s) | Summary | Recommendation |
|----|----------|----------|-------------|---------|----------------|
| A1 | Ambiguity | LOW | tasks T037 | Semaphore↔canonical repo transport left to implement | Keep; document in runbook at install time |
| C1 | Coverage | LOW | tasks T024 | Laptop `adb`/`fastboot --version` without touching Pixel | Keep; useful non-blocking smoke |
**Resolved since prior analyze:** I1 (SSH vs `sudo -iu` smoke), I2 (P1 list includes pubkey + verify login).
No CRITICAL or HIGH findings. No constitution MUST violations. No 002/003 / PVE / LOS build / Pixel leakage.
## Coverage Summary
| Requirement Key | Has Task? | Task IDs |
|-----------------|-----------|----------|
| FR-001 … FR-010 | Yes | (unchanged; 100%) |
| SC-001 … SC-007 | Yes | (unchanged; 100%) |
## Constitution Alignment Issues
None.
## Unmapped Tasks
None material (setup / preflight / polish remain support tasks).
## Metrics
| Metric | Value |
|--------|-------|
| Total Requirements (FR+SC) | 17 |
| Total Tasks | 48 |
| Coverage % | **100%** |
| Ambiguity Count | 1 (LOW, intentional) |
| Duplication Count | 0 |
| Critical Issues Count | **0** |
| High Issues Count | **0** |
## Next Actions
**STOP — freeze documentary cycle for `001`.**
- Do **not** regenerate tasks or re-plan `002`.
- Do **not** `/speckit-implement` until `rom-dev01` has working SSH.
- When VM exists: implement T001–T048 in order (P1 then P2).
## Remediation
No further remediation recommended before freeze.
+186
View File
@@ -0,0 +1,186 @@
# 001 host runbook — `rom-dev01`
Living notes for feature `001-development-environment`.
## Access (laptop → host)
| Item | Value | Notes |
|------|-------|-------|
| Project name | `rom-dev01` | Guest hostname set 2026-09-13 |
| DNS / IP | **`a16.ssystems.de`** (preferred) / `a15.ssystems.de` → `49.13.104.83` | |
| SSH port | **37037** | `:22` closed |
| Admin | `root` (**default**) | `ssh a16` / `a16.ssystems.de` / `a16-root` |
| Build user | `android-build` (uid/gid **1000**) | `ssh a16-build` (also `rom-dev01`, `a15`) |
| Laptop private key | `~/.ssh/id_ed25519_rom_dev01` | also YubiKey PIV in `authorized_keys` |
| Client SSH | `docs/ssh/config` | Include from `~/.ssh/config` |
| Web UI | `https://a16.ssystems.de/` | nginx + wildcard + HTTP Basic |
### OpenSSH / Cursor
```sshconfig
Host a16 a16.ssystems.de a16-root
HostName 49.13.104.83
User root
Port 37037
IdentityFile ~/.ssh/id_ed25519_rom_dev01
IdentitiesOnly yes
Host a16-build
HostName 49.13.104.83
User android-build
Port 37037
IdentityFile ~/.ssh/id_ed25519_rom_dev01
IdentitiesOnly yes
```
Verify:
```bash
ssh a16 'whoami; hostname' # root @ rom-dev01
ssh a16-build 'whoami' # android-build
```
### SSH password auth
Effective `sshd -T`: **`passwordauthentication no`** (Ubuntu
`/etc/ssh/sshd_config.d/60-cloudimg-settings.conf` overrides
`PasswordAuthentication yes` in main `sshd_config`). Pubkey-only in practice.
Do **not** edit Salt-/cloud-managed sshd unless required; current effective
policy already matches the desired end state. YubiKey PIN ≠ SSH password.
## Coexistence with `saltenv=base`
If a Salt-/base-managed file conflicts with project needs: **stop and report**.
Do not silently replace Salt-managed settings.
Observed: `salt-minion` **inactive**; many company pubkeys already in
`/root/.ssh/authorized_keys` (base provision). Project added:
`id_ed25519_rom_dev01` + YubiKey PIV for `root` and `android-build`.
Rollback SSH key: remove the ed25519 line from `authorized_keys` on host;
delete laptop `~/.ssh/id_ed25519_rom_dev01` if retiring.
## Account model
| Account | Role |
|---------|------|
| `root` | packages, users, services, firewall observation |
| `android-build` | all Android builds, `/srv/rom` writes, Spec Kit meta tree |
| future Gitea/Semaphore | least privilege; **never** build Android as root |
## Paths
| Path | Purpose | Mode (approx) |
|------|---------|----------------|
| `/home/android-build/sunfish-a16-rom` | Meta Spec Kit tree | user-owned |
| `/srv/rom/src` | Android source trees | `android-build` |
| `/srv/rom/references` | Reference archives | `android-build` |
| `/srv/rom/tooling` | Host tooling — **not** Spec Kit checkout | `android-build` |
| `/srv/rom/ccache` | `CCACHE_DIR` (50G) | `android-build` |
| `/srv/rom/artifacts` | Artifact handoff to laptop | `android-build` |
| `/srv/rom/logs` | Preflight / toolchain / smoke logs | `android-build` |
| `/srv/rom/secrets` | Restricted secrets | `700 root:root` |
## Preflight summary (2026-09-13)
Full log: `/srv/rom/logs/preflight/preflight-latest.txt`
- Ubuntu 24.04.4 LTS / Linux 6.8.0-136-generic
- 8 vCPU (host CPU E3-1275 v5), **62 GiB** RAM, **32 GiB** swap (`/swapfile`)
- Disk: **1.9T** ext4 on `/` (~2T vda)
- NIC: `ens18` only — `49.13.104.83/29`, default via `49.13.104.81` (public-only)
- Firewall: ufw inactive; iptables ACCEPT (no flush)
- Listening: SSH `0.0.0.0:37037`, systemd-resolved localhost DNS
- apt: Ubuntu noble archives
## Toolchain (US2)
- Package log: `/srv/rom/logs/toolchain-packages.txt` (LOS wiki sunfish list +
`openjdk-17-jdk`, `python-is-python3`)
- `repo` in `~/bin/repo` (launcher 2.65)
- `CCACHE_DIR=/srv/rom/ccache`, `USE_CCACHE=1`, size **50G**, compression on
- Env in `android-build` `.profile` / `.bashrc`
- Smoke: `/srv/rom/logs/toolchain-smoke.txt`
- **FR-007 / build parallelism (use the HW):** guest has **8 vCPU / ~62 GiB /
32 GiB swap**. Default Android builds: **`mka … -j8`** (match vCPU count).
Do **not** leave conservative `-j4` unless diagnosing OOM. Watch `load` /
`available` RAM; if linking OOMs, drop to `-j6` temporarily — not below
without a written reason. Host-side: Proxmox CPU steal can waste cycles;
prefer dedicated/pinned vCPUs when practical (outside this repo).
Wrapper: `/srv/rom/logs/003-build/run-brunch.sh` (`-j8`).
- **nsjail / build sandbox (Ubuntu 24.04):** AppArmor
`kernel.apparmor_restrict_unprivileged_userns=1` put nsjail into profile
`unprivileged_userns` and DENY `mount`/`setgid` → Soong message
`Build sandboxing disabled due to nsjail error`. Fix (scoped, not global
sysctl): `/etc/apparmor.d/nsjail-android-build` (`userns` +
`flags=(unconfined)` for
`/srv/rom/src/lineage/prebuilts/build-tools/linux-{x86,arm64}/bin/nsjail`).
Evidence: `/srv/rom/logs/nsjail-apparmor-fix.txt`. Re-probe on next Soong
start (current `mka bacon` already started with sandbox off).
## Thin client / artifacts (US5)
- Cursor Remote SSH: Host `rom-dev01` →
`/home/android-build/sunfish-a16-rom` as `android-build`
- Dummy rsync: host `/srv/rom/artifacts/dummy-001.txt` → laptop
`Workspaces/sunfish-a16-rom/.local-artifacts/` (gitignored path preferred)
- Pixel USB: **adb/fastboot on laptop only**; no USB passthrough to VM
## Gitea / Ansible / Semaphore
Independent of SSYSTEMS Salt/GitLab. All on `rom-dev01`.
| Service | Bind | Public URL |
|---------|------|------------|
| nginx | `0.0.0.0:80/443` | TLS + HTTP Basic gate |
| Gitea 1.27.3 | `127.0.0.1:3000` | https://a16.ssystems.de/ |
| Semaphore 2.19.12 | `127.0.0.1:3001` | https://a16.ssystems.de/semaphore/ |
| Ansible | host CLI | core 2.16.3 |
- Site: `/etc/nginx/sites-available/a16.ssystems.de`
- TLS: company wildcard `*.ssystems.de` (Sectigo) at
`/etc/nginx/ssl/ssystems.de/{fullchain,privkey}.pem`
Source (laptop, not in this repo):
`buildout-saar-docker/salt/srv/files/etc/pki/tls/{certs,private}/wildcard_.ssystems.de-*`
- HTTP Basic: `/etc/nginx/.htpasswd` — credentials on host only:
`/srv/rom/secrets/web-htpasswd.txt` (user `sunfish`)
- Unit: `gitea.service` — data `/var/lib/gitea`, config `/etc/gitea/app.ini`
(`ROOT_URL=https://a16.ssystems.de/`)
- Unit: `semaphore.service` — data `/var/lib/semaphore`, config `/etc/semaphore/config.json`
(`web_host=https://a16.ssystems.de/semaphore`, `web_root=/semaphore`)
nginx: `location /semaphore/` → `proxy_pass` **without** trailing URI
(must preserve `/semaphore` prefix or UI assets 404 → blank page)
- App secrets (host only, never Git): `/srv/rom/secrets/gitea-admin.txt`,
`/srv/rom/secrets/semaphore-admin.txt`
- Admin user (both apps): `sunfish-admin` (after nginx Basic)
- Git transport: **HTTPS via nginx** (Gitea still `DISABLE_SSH=true`; no public `:2222`)
- Repos (private): `sunfish-admin/{sunfish-a16-rom,sunfish-rom-manifest,sunfish-rom-build,sunfish-rom-patches}`
- Automation tree: `/home/android-build/sunfish-rom-build` (not inside Spec Kit meta)
- Semaphore project `sunfish-rom` template `env-smoke` clones **canonical**
`sunfish-rom-build` from Gitea and runs `ansible/smoke.yml` unprivileged
- Evidence: `/srv/rom/logs/ansible-smoke.txt`, `/srv/rom/logs/semaphore-smoke.txt`
- Off-VM mirrors (laptop): `/home/aventura/Archives/sunfish-rom-mirrors/*.git`
- No SSH tunnels needed for UIs anymore
### Accounts (reminder)
- **root**: install packages/services only
- **android-build**: Android builds, meta + automation working trees
- **git** / **semaphore**: service users for Gitea / Semaphore
- Do **not** build Android as root (LOS/AXP practice)
## Isolation checklist
- [x] Public NIC only (`ens18` /29) — no internal SSYSTEMS NIC required
- [x] App backends remain loopback (`:3000` / `:3001`); only nginx is public
- [x] Workflow uses public DNS `a16.ssystems.de` + SSH port 37037 (no VPN)
- [x] Gitea/Semaphore behind nginx HTTPS + HTTP Basic (`htpasswd`)
## Off-VM mirror
- Laptop: `/home/aventura/Archives/sunfish-rom-mirrors/*.git`
- Host: `/srv/rom/artifacts/gitea-mirrors/` (token stays on host; laptop only rsyncs)
## Handoff
**`001` complete → proceed to `002` (AXP reference capture).**
Do **not** start LineageOS `repo sync` / build as part of `001`.
+781
View File
@@ -0,0 +1,781 @@
001 plan review pack — for ChatGPT
Generated: 2026-08-09T12:23:59+02:00
Constitution: 0.1.5 | Active: specs/001-development-environment
================================================================================
FILE: specs/001-development-environment/plan.md
================================================================================
# 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
# meta Spec Kit tree (staging copy, then Gitea clone)
# e.g. /home/android-build/sunfish-a16-rom and/or /srv/rom/tooling/...
```
### Meta repository content (laptop staging → Gitea)
```text
sunfish-a16-rom/ # Spec Kit + docs (this tree)
├── .specify/
├── specs/
├── docs/
├── AGENTS.md
└── …
```
Companion empty repos on Gitea (no AOSP trees):
`sunfish-rom-manifest`, `sunfish-rom-build`, `sunfish-rom-patches`.
**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; **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**: `sudo -iu android-build` → verify tools; **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
```
Do **not** assume `git clone https://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 access: **loopback / non-public bind + SSH tunnel** from laptop
(decision fixed for `001` unless implement discovers a hard conflict — then
stop/report).
- 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` (meta Spec Kit/docs) — push existing history
2. `sunfish-rom-manifest` — empty
3. `sunfish-rom-build` — empty (future Ansible/scripts home)
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):
```text
Gitea repos → bare mirrors on laptop (or equivalent off-VM path)
```
Borg / full Gitea disaster recovery is **later**, not required to close `001`.
Requirement is **Git history** redundancy, not VM image backup.
## 13. Ansible bootstrap (P2)
- Install Ansible on `rom-dev01` for **project environment** automation.
- Keep playbooks in meta repo or `sunfish-rom-build` once created.
- No plaintext secrets in Git (use local ansible vault / restricted files outside
Git if needed in `001` — prefer deferred secrets).
## 14. Semaphore bootstrap (P2)
- Install Semaphore as UI over Ansible.
- Same exposure model as Gitea (loopback/tunnel).
- First integration: **small localhost/environment smoke playbook only** —
prove Semaphore can run Ansible. **Not** 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. Cursor Remote SSH → host workspace as `android-build` (or documented path)
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 | `id android-build`; login shell works |
| FR-002 | LOS/AOSP host deps + ccache | clean-shell: `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. Create `/srv/rom` + permissions
4. Install build prerequisites (current LOS/AOSP docs)
5. `repo` / `git` / `git-lfs` / ccache config
6. Copy laptop staging meta repo to host (preserve `.git`)
7. Clean-shell toolchain smoke
8. Cursor Remote SSH smoke
9. 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.
================================================================================
FILE: specs/001-development-environment/research.md
================================================================================
# 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 a
localhost/environment smoke playbook only.
**Rationale**: Semaphore is orchestration UI, not a public portal; Android build
pipeline is explicitly out of scope for `001`.
**Alternatives considered**: Public Semaphore — rejected. Full ROM pipeline in
Semaphore — deferred past `003` manual first build.
## R3 — Off-VM Git history backup
**Decision**: Laptop-side **bare mirrors** of the four Gitea repos for `001`
(SC-007). Borg/full Gitea DR later.
**Rationale**: Spec requires off-VM copy of project Git history, not a complete
backup platform. Simplest path that meets SC-007.
**Alternatives considered**: Borg-only — optional later. Second server — not
needed for `001`.
## 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 first (rsync/scp/bundle
preserving `.git`), install Gitea, create repos, push, then mark Gitea
canonical. Keep laptop staging until mirror verified.
**Rationale**: Avoid chicken/egg `git clone` against non-existent Gitea.
## 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.
================================================================================
FILE: specs/001-development-environment/data-model.md
================================================================================
# 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 |
### Path root `/srv/rom`
| Child | Purpose | Populated in 001? |
|-------|---------|-------------------|
| `src/` | Future Android trees | Empty (no sync) |
| `references/` | Future AXP/reference | Empty |
| `tooling/` | Scripts/ansible helpers | 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) |
| 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.
================================================================================
FILE: specs/001-development-environment/quickstart.md
================================================================================
# 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
- SSH to `rom-dev01` as admin works
- You are ready to create `android-build` and `/srv/rom` (P1)
- Laptop has `rsync`, `ssh`, Cursor Remote SSH
## P1 validation path
### 1. Preflight captured
```bash
# On host (admin) — exact commands filled at implement time
# Expect: Ubuntu 24.04, disk/RAM/swap notes, firewall summary saved
```
### 2. Switch to build user
```bash
sudo -iu android-build
pwd # documented workspace
```
### 3. Toolchain smoke (clean shell)
```bash
git --version
git lfs version
repo --version
python3 --version
ccache -s
# plus java/other tools required by current LOS/AOSP host docs
```
Expected: all commands succeed without sudo.
### 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
# path documented at implement — must contain .git and specs/
test -d /path/to/sunfish-a16-rom/.git
test -f /path/to/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 tunnel example (ports chosen at implement):
# ssh -L 3000:127.0.0.1:3000 USER@ROM_DEV01
# curl -fsS http://127.0.0.1:3000/ → success via tunnel
```
Confirm UI not relied upon as public Internet service.
### 9. Four repositories
In Gitea: `sunfish-a16-rom`, `sunfish-rom-manifest`, `sunfish-rom-build`,
`sunfish-rom-patches`. Meta repo history visible after push.
### 10. Off-VM mirror
On laptop: bare clones/mirrors of all four; `git fsck` or fetch succeeds.
### 11. Semaphore smoke
Semaphore runs the environment smoke playbook successfully (localhost/host
check only — not Android build).
### 12. Restart survival
```bash
sudo systemctl restart gitea # unit names as installed
sudo systemctl restart <semaphore>
# optional: reboot when safe; confirm SSH + services return
```
### 13. Isolation checklist
- No required internal NIC for workflow
- Gitea/Semaphore binds documented as non-public
- SSH still works after firewall changes
- Day-to-day docs do not require company VPN/internal DNS
## Done means
`001` complete → start **`002`** (AXP capture / NeoBackup).
Do **not** start Lineage `repo sync` / build as part of `001` success.
================================================================================
FILE: specs/001-development-environment/contracts/README.md
================================================================================
# 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.
+222
View File
@@ -0,0 +1,222 @@
001 tasks review pack — for ChatGPT
Generated: 2026-08-09T12:30:10+02:00
================================================================================
FILE: specs/001-development-environment/tasks.md
================================================================================
# 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)
- [ ] T001 Confirm active feature is `specs/001-development-environment` via `.specify/feature.json` and that constitution is `0.1.5` in `.specify/memory/constitution.md`
- [ ] T002 [P] Create host-facing doc stub `docs/001-host-runbook.md` in meta repo (preflight results, paths, Gitea tunnel notes — fill during implement)
- [ ] 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.
- [ ] 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`
- [ ] 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
- [ ] 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 documented workspace; `/srv/rom` owned correctly; meta tree at fixed path.
**Independent test**: `sudo -iu android-build` works; `/srv/rom/artifacts` writable; `/home/android-build/sunfish-a16-rom` contains Spec Kit tree (after T012).
### Implementation
- [ ] T007 [US1] Create unprivileged user `android-build` with home `/home/android-build` (admin); document uid/gid in `docs/001-host-runbook.md`
- [ ] T008 [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`
- [ ] T009 [US1] Verify: `namei -l /srv/rom /srv/rom/artifacts` and `sudo -iu android-build touch /srv/rom/artifacts/.write_test` succeeds; no `chmod 777`
- [ ] T010 [US1] Document account model (admin vs `android-build` vs service users) in `docs/001-host-runbook.md` — never build Android as root
- [ ] T011 [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`
- [ ] T012 [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**: `sudo -iu android-build` → `git`, `git lfs`, `repo`, `python3`, `ccache -s`, and LOS-doc Java/prereqs succeed.
### Implementation
- [ ] T013 [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`
- [ ] T014 [US2] Install/configure `repo` for `android-build` per current docs; verify `sudo -iu android-build repo --version`
- [ ] T015 [US2] Ensure `git` + `git-lfs` for `android-build`; verify `sudo -iu android-build git lfs version`
- [ ] T016 [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`
- [ ] T017 [US2] Verify: `sudo -iu android-build ccache -s` shows configured cache dir
- [ ] T018 [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
- [ ] T019 [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 + artifact rsync proven; Pixel stays on laptop USB.
**Independent test**: Remote SSH edit on host; dummy artifact SHA256 matches after rsync.
### Implementation
- [ ] T020 [US5] Document Cursor Remote SSH → `/home/android-build/sunfish-a16-rom` in `docs/001-host-runbook.md` and `specs/001-development-environment/quickstart.md` (update if needed)
- [ ] T021 [US5] Create `/srv/rom/artifacts/dummy-001.txt` as `android-build`; record `sha256sum` in `/srv/rom/logs/rsync-smoke.txt`
- [ ] T022 [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)
- [ ] T023 [US5] Document adb/fastboot-on-laptop / no USB passthrough rule in `docs/001-host-runbook.md` (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; laptop pull mirrors; unprivileged Semaphore smoke via Ansible in `sunfish-rom-build`.
**Independent test**: Four repos on Gitea; meta history pushed; laptop `git clone --mirror` works; Semaphore runs unprivileged smoke.
### Implementation
- [ ] T024 [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`
- [ ] T025 [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`
- [ ] T026 [US3] Verify: local `curl` to Gitea UI succeeds; confirm not relying on public Internet exposure; record bind address in `docs/001-host-runbook.md`
- [ ] T027 [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`
- [ ] T028 [US3] Create Gitea repos: `sunfish-a16-rom`, `sunfish-rom-manifest`, `sunfish-rom-build`, `sunfish-rom-patches`
- [ ] T029 [US3] Push `/home/android-build/sunfish-a16-rom` history to Gitea `sunfish-a16-rom`; set `origin` to Gitea; verify remote in that directory
- [ ] T030 [US3] Seed minimal structure in `sunfish-rom-build` for Ansible (e.g. `ansible/` playbooks path) — **not** inside Spec Kit meta repo
- [ ] T031 [US3] Install Ansible on `rom-dev01`; document version in `docs/001-host-runbook.md`
- [ ] T032 [US3] Add unprivileged smoke playbook in `sunfish-rom-build` (gather_facts + asserts for `/srv/rom`, `android-build`, toolchain); **no become/sudo secrets**
- [ ] T033 [US3] Run smoke playbook successfully via `ansible-playbook` CLI as documented non-root/project user; log output to `/srv/rom/logs/ansible-smoke.txt`
- [ ] T034 [US3] Install Semaphore; bind like Gitea (loopback/tunnel); no public exposure; document in `docs/001-host-runbook.md`
- [ ] T035 [US3] Wire Semaphore to run the **same unprivileged** smoke playbook only; execute once successfully; evidence in `/srv/rom/logs/semaphore-smoke.txt` (FR-005)
- [ ] T036 [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)
- [ ] T037 [US3] Confirm laptop staging `/home/aventura/Workspaces/sunfish-a16-rom` is **retained** until T029+T036 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
- [ ] T038 [US4] Document interfaces/routes: project workflow requires only public NIC — `docs/001-host-runbook.md` + evidence in `docs/001-verification-matrix.md` (SC-006)
- [ ] T039 [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`
- [ ] T040 [US4] Re-verify Gitea/Semaphore binds remain non-public; SSH admin + tunnel still work — update `docs/001-verification-matrix.md` FR-009/SC-006
- [ ] T041 [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
- [ ] T042 [P] Fill all FR-*/SC-* evidence rows in `docs/001-verification-matrix.md` (including SC-005: no PVE/qm/Salt VM tasks were performed)
- [ ] T043 [P] Align `specs/001-development-environment/quickstart.md` with final ports/paths/tunnel commands actually used
- [ ] T044 Restart Gitea (+ Semaphore) via systemd; confirm recovery; optional reboot when safe; note results in `/srv/rom/logs/restart-smoke.txt`
- [ ] T045 Declare handoff in `docs/001-host-runbook.md`: **`001` complete → proceed to `002`**, do not start Lineage sync/build
- [ ] T046 Ensure no secrets/tokens landed in `/home/android-build/sunfish-a16-rom` or `sunfish-rom-build` Git history (`git grep` / review)
---
## Dependencies & story order
```text
Phase 1 (laptop docs)
↓
Phase 2 preflight (host)
↓
US1 workspace + /srv/rom + meta copy
↓
US2 toolchain ──────────────┐
↓ │
US5 thin-client smokes ←───┘ (needs artifacts + meta path)
↓
US3 Gitea → repos → Ansible in sunfish-rom-build → Semaphore → laptop mirrors
↓
US4 isolation hardening/validation
↓
Polish / handoff → STOP (wait was already for implement; next feature 002)
```
**MVP (minimum useful host)**: complete through US2 + US5 (T007–T023) even if Gitea delayed — but **formal `001` done** requires P2 (US3+US4+polish) per plan.
## Parallel opportunities
- T002/T003 on laptop before VM
- After T008: T010 docs can parallel T011 copy if disk ready
- T013–T015 package/`repo`/`git-lfs` partly serial (apt) then T016 ccache
- T042/T043 polish docs in parallel after evidence exists
## FR/SC coverage map
| ID | Tasks |
|----|-------|
| FR-001 | T007–T010 |
| FR-002 | T013–T017, T019 |
| FR-003 | T008–T009, T012 |
| FR-004 | T024–T029 |
| FR-005 | T030–T035 |
| FR-006 | T020–T023 |
| FR-007 | T018 |
| FR-008 | T011, T029 |
| FR-009 | T038–T041 |
| FR-010 | T036–T037 |
| SC-001 | T019 |
| SC-002 | T021–T022 |
| SC-003 | T028–T029 |
| SC-004 | T020, T023, T045 |
| SC-005 | T005, T042 |
| SC-006 | T038–T041 |
| SC-007 | T036 |
## Implementation strategy
1. Do **not** execute host tasks until SSH to `rom-dev01` works.
2. After this `tasks.md` is approved: `/speckit-analyze` → STOP.
3. `/speckit-implement` follows P1 then P2; stop/report on Salt conflicts.
4. Do not plan `002` until `001` implement + verification complete.
================================================================================
Micro-adjustments applied to plan (summary)
================================================================================
- Meta path fixed: /home/android-build/sunfish-a16-rom
- Ansible in sunfish-rom-build only
- Semaphore smoke unprivileged (no sudo secrets)
- Off-VM mirror: laptop pull only
- No public :2222 / no blind sshd edits; transport documented at implement
+38
View File
@@ -0,0 +1,38 @@
# 001 verification matrix — FR / SC evidence
Map each requirement from `specs/001-development-environment/spec.md` to
observable evidence. Leave blank until verified on `rom-dev01`.
## Functional requirements
| ID | Requirement (short) | Evidence (command/path/doc) | Status |
|----|---------------------|-----------------------------|--------|
| FR-001 | `android-build` + documented workspace | uid 1000; `/home/android-build/sunfish-a16-rom`; runbook | pass |
| FR-002 | Android/LOS prereqs + ccache | `/srv/rom/logs/toolchain-packages.txt` + `toolchain-smoke.txt` | pass |
| FR-003 | `/srv/rom` layout | namei + write test artifacts | pass |
| FR-004 | Private Gitea for project repos | loopback :3000; 4 private repos; secrets under /srv/rom/secrets/ | pass |
| FR-005 | Ansible + Semaphore UI | ansible 2.16.3; Semaphore task env-smoke success | pass |
| FR-006 | Cursor Remote SSH + rsync + laptop flash workflow doc | runbook Host `rom-dev01`; rsync smoke; adb on laptop | pass |
| FR-007 | Conservative parallelism defaults (~6 vCPU) | measured 8/62/32; `-j` deferred to 003 | pass |
| FR-008 | Spec Kit tree on host | `/home/android-build/sunfish-a16-rom` SPEC_OK | pass |
| FR-009 | Guest isolation posture | public NIC only; UIs on 127.0.0.1 | pass |
| FR-010 | Off-VM backup/mirror of Gitea repos | laptop Archives/sunfish-rom-mirrors/*.git fsck OK | pass |
## Success criteria
| ID | Outcome (short) | Evidence | Status |
|----|-----------------|----------|--------|
| SC-001 | Clean `android-build` SSH has toolchain for `003` | `toolchain-smoke.txt` (git/lfs/repo/ccache/java) | pass |
| SC-002 | `/srv/rom/artifacts` writable handoff | dummy-001 SHA256 match laptop `.local-artifacts/` | pass |
| SC-003 | Four Gitea repos ready (no company GitLab) | sunfish-admin/* on local Gitea | pass |
| SC-004 | Cold-start workflow doc | quickstart.md + host-runbook | pass |
| SC-005 | No PVE/VM create/reconfig in this feature | no qm/PVE/Salt VM work in 001 | pass |
| SC-006 | Isolation checklist passes | runbook isolation checklist | pass |
| SC-007 | Off-VM mirror demonstrated once | rsync bare mirrors + fsck | pass |
Status values: `pending` | `pass` | `fail` | `blocked`
## Task cross-check
Track completion in `specs/001-development-environment/tasks.md`. This matrix
is evidence, not a second task list.
+15
View File
@@ -0,0 +1,15 @@
# 002 gate checklist (handoff → 003)
Date: 2026-09-13
| Gate | Status | Evidence |
|------|--------|----------|
| GOLDEN labeled; do-not-flash | OK | `docs/device-roles.md` — serial `11071JEC203839`; agents must not modify |
| LAB labeled | OK | serial `11231JEC207477` (iodé baseline) |
| LAB factory rescue doc + verified zip | OK | `docs/002-lab-stock-rescue.md`; host SHA-256 **VERIFY_OK** |
| Essential GOLDEN inventory + banking layout | OK | `docs/002-golden-inventory.md` (gaps = re-query; Yuh FAIL documented) |
| NeoBackup / wipe GOLDEN | N/A | Not a gate |
| FLAG_SECURE / call-record source | Deferred | T021 — re-query GOLDEN later; not blocking `003` vanilla |
| Offline AXP archive rsync | In progress | ~18G on host of ~38G laptop; not a flash gate |
**Handoff:** `002` P1 complete for `003` purposes → flash/build **LAB only**.
+108
View File
@@ -0,0 +1,108 @@
# GOLDEN inventory (AXP.OS oracle) — redacted
**Device:** GOLDEN sunfish (main phone) — **DO NOT FLASH / wipe / modify**.
**Serial:** `11071JEC203839`
**Role:** Live **AXP.OS Pro 22.2 / Android 15** compatibility oracle.
**Access:** Non-destructive `adb` only (sampled 2026-09-13).
**Raw artifacts:** `/srv/rom/references/axp/golden/2026-09-13/` on `rom-dev01`
(includes Magia DenyList screenshots, Yuh logcat; Git = this summary only).
## Identity (measured)
| Field | Value |
|-------|--------|
| Serial | `11071JEC203839` |
| Owner user | `Andres` (user 0) |
| `ro.build.display.id` | `BP1A.250505.005` |
| `ro.build.version.release` | `15` |
| `ro.build.version.security_patch` | `2026-03-03` |
| AXP / Lineage identity | `ro.lineage.version=22.2-2026.003-sunfish-pro` (`releasetype=PRO`) |
| Bootloader | **unlocked** (`flash.locked=0`, `verifiedbootstate=orange`) |
| Magisk | Binary **29.0** (`29000`) at `/system_ext/bin/magisk`; UI app named **Magia** |
## Users / work profile
| User | Name | Notes |
|------|------|-------|
| 0 | Andres | Owner |
| 10 | Island | Profile owner = `com.oasisfeng.island.fdroid` 6.4.2 |
After owner’s 2026-09-13 reclone experiment: `com.yuh` and
`com.swissquote.android` present on **both** user 0 and user 10
(Island reinstall of Yuh `firstInstallTime` user10 ≈ 21:48).
## Essential packages (measured)
| Area | Package / path | Version | Notes |
|------|----------------|---------|--------|
| microG GmsCore | `com.google.android.gms` | **0.3.15.250932** | System base MicrogGmsCore_GH; sig short `73aaa390` |
| Phonesky (AXP) | `/system/priv-app/Phonesky_AXP-OS` | **50.2.12-24** | Sig short `e05f779d` |
| GSF Proxy | `/system_ext/app/GsfProxy_GH` | `v0.1.0` | |
| Island | `com.oasisfeng.island.fdroid` | `6.4.2-d46911c9` | Spec text said “Insular”; measured = **Island** |
| Aurora Store | `com.aurora.store` | `4.8.4` | |
| Yuh | `com.yuh` | `1.45.11` (vc 535) | See attestation section |
| Swissquote | `com.swissquote.android` | `9.9.0` | Owner reports still OK |
| Aosmium (AXP) | `org.axpos.aosmium` | `148.0.7778.178` | Not on DenyList |
| Magisk modules list | `/data/adb/modules` | — | Not readable without root shell — **re-query** if needed |
## Magia DenyList (from UI screenshots 2026-09-13)
Checked packages (package-level; process expand not fully visible in shots):
| App | Package |
|-----|---------|
| BBVA Spain | `com.bbva.bbvacontigo` |
| BOOM | `com.boom.android.mobile2` |
| CieID | `it.ipzs.cieid` |
| Device configuration | `android.autoinstalls.config.google.nexus` |
| DKB | `com.dkbcodefactory.banking` |
| Identifica | `it.sielte.sielteidentify` |
| N26 | `de.number26.android` |
| Scalable Capital | `capital.scalable.droid` |
| Swissquote | `com.swissquote.android` |
| Webank | `com.opentecheng.android.webank` |
| Wise | `com.transferwise.android` |
| Yuh | `com.yuh` |
Visible unchecked (partial): Aegis, Amarok, Amaze, Aosmium.
Owner notes: most DenyList banking apps still pass; **Yuh fails** for ~weeks.
Extra Magia DenyList/host checks + Island uninstall/reclone did **not** fix Yuh.
## Compatibility / attestation (observational)
| Check | Status | Evidence |
|-------|--------|----------|
| Swissquote | **PASS** (owner report) | Still used; on DenyList |
| Other DenyList banks (N26, DKB, …) | **PASS** (owner: “casi todos”) | UI DenyList |
| Yuh | **FAIL** — hardware attestation / “certificate is invalid” | Owner UI + logcat ~21:51–21:52 |
| Play Integrity token path | Client path still runs | `Finsky requestIntegrityToken() finished for com.yuh`; DroidGuard `pia_attest_e1` for `com.yuh` |
| KeyMint / TEE | Errors concurrent with Yuh attempt | `keystore2`: `HARDWARE_TYPE_UNAVAILABLE` (IRPC / `TRUSTED_ENVIRONMENT`); `addRngEntropy` → `DEAD_OBJECT` / KeyMint `-1000` |
Interpretation for later `006`/`007` (not a GOLDEN fix): DenyList alone is
insufficient for Yuh’s hardware-attestation path; KeyMint/TEE remote-provisioning
errors correlate with the failure window. Project production root path remains
**KernelSU-Next (+ SusFS) or ReSukiSU evaluation** — Magisk/Magia is oracle-only.
Call recording: GOLDEN Secure dump shows native EXTENDROM
`extendrom_call_recording=1` (see `008` — port `er.sh` F_CALLREC, not Magisk
RRO). FLAG_SECURE override: not identified as AXP catalog on GOLDEN; project
`009` is native DO.
## Banking layout (oracle)
Historical target order (AXP workflow; Magisk DenyList → Island):
1. Aurora Store → install
2. Do not open
3. Magia DenyList (incl. processes) before first process
4. Clone into Island
5. First launch in Island
6. Phonesky/Play in Island as needed
Observed: Yuh+Swissquote intended in Island; after 2026-09-13 debug, both also
on owner user 0 — clean up is owner’s choice; agents must not uninstall.
## Policy
No flash, wipe, Magisk/Magia changes, or app uninstalls by agents — sample only.
+42
View File
@@ -0,0 +1,42 @@
# LAB baseline (iodé) — measured 2026-09-13
**Role:** LAB — second sunfish for flashing our ROM.
**Serial:** `11231JEC207477`
**As found:** iodé / Lineage `7.8-20260902-sunfish`, Android **16**, patch `2026-08-01`.
**Bootloader:** locked (`ro.boot.flash.locked=1`).
**Policy:** OK to flash/wipe for experiments after factory rescue is in place.
This is **not** the AXP GOLDEN oracle. Banking apps / Island / microG here are
LAB-side copies or leftovers — do not treat as AXP Pro parity evidence.
## Snapshot (text-only)
| Item | Value |
|------|--------|
| `ro.lineage.version` | `7.8-20260902-sunfish` |
| `ro.build.version.release` | `16` |
| microG GmsCore | `0.3.16.252432` |
| FakeStore / vending | `0.3.16.40226` |
| GsfProxy | `v0.1.0` |
| Island | `6.4.2-d46911c9` (user 10 `myisla`) |
| Magisk/KSU | not found |
Raw logs (moved/copied from early mislabel): host
`/srv/rom/references/axp/lab/2026-09-13-iodes-baseline/` when present; else
`/srv/rom/references/axp/golden/2026-09-13/` (same content — LAB).
Factory rescue: [`002-lab-stock-rescue.md`](002-lab-stock-rescue.md).
## Yuh on LAB (log peek 2026-09-13 ~22:15)
Launched on **user 0** (not Island). No Magisk. Bootloader **locked**.
Same core error as GOLDEN within ~4s of open:
`keystore2`: RKP/IRPC for `TRUSTED_ENVIRONMENT` → `Error::Km(HARDWARE_TYPE_UNAVAILABLE)`
Also: Yuh loads `libts.so`; SELinux denials probing `getenforce` / `ps` / `/cache` (root-detect style).
No Magisk DenyList involved on LAB → failure is **not** “only Magisk visible”.
Raw: host `…/lab/2026-09-13-iodes-baseline/yuh-2026-09-13/`.
+63
View File
@@ -0,0 +1,63 @@
# LAB stock factory rescue — Pixel 4a (`sunfish`)
**Scope: LAB only.** Never use these steps on GOLDEN.
## Source (measured 2026-09-13)
Google’s public factory listing for sunfish is pruned. The former CDN URL now
returns **HTTP 404**:
`https://dl.google.com/dl/android/aosp/sunfish-tq3a.230805.001.s1-factory-82902ea9.zip`
Canonical build identity (final Android 13 / TQ3A, widely cross-checked):
| Field | Value |
|-------|--------|
| Build | `TQ3A.230805.001.S1` |
| File | `sunfish-tq3a.230805.001.s1-factory-82902ea9.zip` |
| Size | `2276267484` bytes |
| SHA-256 | `82902ea98c80f1a22706279a30d2bc46aa55cbe60703c9a02ba043bd052c6d99` |
| Primary index | https://developers.google.com/android/images (sunfish may be empty) |
| Fallback (measured) | Internet Archive item [`sunfish-tq3a.230805.001.s1`](https://archive.org/details/sunfish-tq3a.230805.001.s1) |
| Cross-check | CalyxOS `vars/sunfish` cites the same URL/hash historically |
Host storage: `/srv/rom/references/axp/lab/`
Download log: `/srv/rom/references/axp/logs/lab-factory-download.txt`
## Before flashing
1. Confirm the USB phone is **LAB** (serial/label) — not GOLDEN.
2. Backup anything you care about on LAB (full wipe).
3. Unlock bootloader only if required and already accepted for LAB.
4. Verify zip SHA-256 before `flash-all`.
```bash
sha256sum -c <<<'82902ea98c80f1a22706279a30d2bc46aa55cbe60703c9a02ba043bd052c6d99 sunfish-tq3a.230805.001.s1-factory-82902ea9.zip'
```
## Flash (factory package)
From an unpacked factory directory on the **laptop** (USB to LAB):
```bash
# device in fastboot; verify serial matches LAB
fastboot devices
cd /path/to/sunfish-tq3a.230805.001.s1
./flash-all.sh # Linux; erases userdata
```
Windows: `flash-all.bat`.
`-w` is typically included in Google’s script (data wipe).
Alternative: https://flash.android.com only if that build remains selectable (LAB only).
## After flash
- Confirm LAB boots stock.
- Re-unlock / re-setup only as needed for later `003` LOS experiments.
- Do **not** relock unless you intend stock-only LAB.
## Gate
FR-002 / SC-002: this doc + **SHA-256-verified** zip on host are required before
experimental `003` flashes on LAB.
+93
View File
@@ -0,0 +1,93 @@
# 003 — Build notes (LOS 23.2 sunfish vanilla)
**Host:** `rom-dev01` as `android-build`
**Tree:** `/srv/rom/src/lineage`
**Artifacts:** `/srv/rom/artifacts/003-…`
**Logs:** `/srv/rom/logs/003-…`
**ccache:** `CCACHE_DIR=/srv/rom/ccache` (export in build shell)
## Parallelism — squeeze the HW
Guest is **8 vCPU / ~62 GiB**. Policy for this host:
| Job | Default | Notes |
|-----|---------|--------|
| `mka bacon` / Soong ninja | **`-j8`** | Match `nproc`; do not leave `-j4` idle |
| `repo sync` | `-j8` OK | Network-bound; `-j4` fine if flaky |
| OOM during link | try `-j6` | Only then; document why |
Do **not** pass `-j` to `brunch` (it eats extra args as variant). Use
`breakfast` + `mka bacon -j8`, or `/srv/rom/logs/003-build/run-brunch.sh`.
If `mpstat` shows high **%steal**, the Proxmox host is oversubscribed — fix
outside this repo (pin/dedicate vCPUs); guest governors usually unavailable.
## Manifest (wiki-verified 2026-09-13)
```bash
mkdir -p /srv/rom/src/lineage && cd /srv/rom/src/lineage
repo init -u https://github.com/LineageOS/android.git -b lineage-23.2 --git-lfs --no-clone-bundle
repo sync -c -j8
```
Primary: https://wiki.lineageos.org/devices/sunfish/build/
Branch `lineage-23.2` listed for sunfish.
## Build (after sync + proprietary blobs)
```bash
export CCACHE_DIR=/srv/rom/ccache
export USE_CCACHE=1
cd /srv/rom/src/lineage
source build/envsetup.sh
breakfast sunfish userdebug
# extract proprietary blobs: TheMuppets lineage-23.2 → vendor/google/sunfish
mka bacon -j8 # do NOT pass -j to brunch; wrapper: /srv/rom/logs/003-build/run-brunch.sh
```
Target expected: `lineage_sunfish-userdebug` (confirm after `breakfast`).
## Metadata to capture on success
- `repo manifest -r` → artifacts dir
- SHA-256 of zip/images
- compressed build log
- `-j` used, wall time, host `nproc`/RAM note
## Status
- [x] repo init (`/srv/rom/src/lineage`, `lineage-23.2`, 2026-09-13)
- [x] repo sync (`finished 2026-09-13T21:27:26Z exit=0`; ~180G; log `/srv/rom/logs/003-sync/sync.log`)
- [x] proprietary blobs policy documented (factory base + AXP/GOS diffs later)
- [x] TheMuppets `lineage-23.2` synced → `vendor/google/sunfish`
- [x] first successful build — `#### build completed successfully` /
`END … exit=0` (2026-09-14T13:52:46Z); j8 segment **06:45:43**
- [x] artifacts + `repo manifest -r` + SHA-256 + `brunch.log.gz` →
`/srv/rom/artifacts/003-vanilla/20260914-lineage-23.2-sunfish/`
(rsync → `.local-artifacts/…` on laptop)
- [x] flash LAB + smoke (`docs/003-flash-lab.md`) — done 2026-09-14;
gate → `004` (`docs/003-gate-handoff.md`)
## R7 note (vanilla `003`)
For the first vanilla build: **factory-equivalent blobs only**. AXP/Graphene
diffs land in later hardening/compat features — not in the clean baseline.
## Proprietary blobs (qué son)
Android open source **no** incluye todo lo que el Pixel necesita para radio,
cámara, firmware, etc. Esos binarios cerrados de Google/Qualcomm se llaman
**proprietary blobs** (o “vendor blobs”). Sin ellos, `brunch sunfish` no
arranca hardware real.
### Decision (owner 2026-09-13) — no overthink
| Layer | Approach |
|-------|----------|
| **Base blobs** | Factory Google / Lineage-equivalent extract (e.g. TheMuppets or `extract-files` from factory) — same hardware binaries stock uses |
| **AXP diffs** | Only sunfish-relevant proprietary/packaging diffs from AXP A15 reference |
| **GrapheneOS diffs** | Historical **sunfish A13** where still applicable; plus ideas from a **currently supported Pixel** when code is shared and still relevant (sunfish is EOL — do not assume every modern GOS change ports) |
Do **not** chase exotic blob sets for location quality; NLP/services matter more than swapping vendor GPS blobs.
+90
View File
@@ -0,0 +1,90 @@
# 003 — Flash LAB (vanilla LOS only)
**LAB serial:** `11231JEC207477`
**Never flash:** GOLDEN `11071JEC203839`
## Artifacts (this build)
Host: `/srv/rom/artifacts/003-vanilla/20260914-lineage-23.2-sunfish/`
Laptop: `.local-artifacts/003-vanilla/20260914-lineage-23.2-sunfish/` (rsynced)
| File | Role | SHA-256 (prefix) |
|------|------|------------------|
| `lineage-23.2-20260914-UNOFFICIAL-sunfish.zip` | sideload ROM | `f89edfe1…5852` |
| `boot.img` | Lineage recovery (flash to `boot`) | `aaf52d20…aad1` |
| `dtbo.img` | required before recovery | `a400e6ae…6327` |
| `vbmeta.img` | copied for reference (wiki path uses dtbo+boot) | `b3223b5d…0735` |
Full digests: `SHA256SUMS` in the same directory. Metadata: `build-metadata.txt`.
## Owner decisions (2026-09-13)
- **Unlock / wipe LAB:** allowed when needed for first custom flash.
- **Stock factory on LAB:** not desired as a planned OS (rescue zip stays on
disk for emergencies only).
- **Banking hard-gate focus:** **Yuh** (+ Swissquote); Revolut is **not** a
hard-gate.
- Vanilla `003` only — no microG / KSU / GApps add-ons on first flash.
## Wiki note (firmware)
Upstream LOS install wiki asks for **stock Android 13** firmware before first
install. LAB currently runs custom A16 (iodé). For this project we flash
vanilla LOS over LAB as approved; if radio/modem misbehaves after smoke, use
`docs/002-lab-stock-rescue.md` then re-flash — **never GOLDEN**.
## Before every fastboot/adb command
```bash
adb devices -l
fastboot devices
# serial must be 11231JEC207477 only
```
```bash
ART="$HOME/Workspaces/sunfish-a16-rom/.local-artifacts/003-vanilla/20260914-lineage-23.2-sunfish"
cd "$ART"
sha256sum -c SHA256SUMS
```
## Unlock (first custom flash)
Unlocking **wipes** LAB — owner-approved.
```bash
adb -s 11231JEC207477 reboot bootloader
fastboot -s 11231JEC207477 devices
fastboot -s 11231JEC207477 flashing unlock # confirm on device
```
## Flash (LOS wiki sunfish path)
```bash
cd "$ART"
fastboot -s 11231JEC207477 flash dtbo dtbo.img
fastboot -s 11231JEC207477 reboot bootloader
fastboot -s 11231JEC207477 flash boot boot.img
# device menu → Recovery
# Recovery → Factory reset → Format data
# Apply update → Apply from ADB
adb -s 11231JEC207477 sideload lineage-23.2-20260914-UNOFFICIAL-sunfish.zip
# Reboot system now (no GApps / no add-ons for vanilla 003)
```
Unofficial zip: recovery may warn about signature — expected; proceed only if
hashes match `SHA256SUMS`.
## After first boot
```bash
adb -s 11231JEC207477 shell getprop ro.lineage.version
adb -s 11231JEC207477 shell getprop ro.build.display.id
# expect lineage 23.2 / sunfish UNOFFICIAL — not AXP, not iodé
```
Then complete `docs/003-smoke-lab.md`.
## Failure
Use [`002-lab-stock-rescue.md`](002-lab-stock-rescue.md) only as **emergency**
brick recovery. Do not touch GOLDEN.
+33
View File
@@ -0,0 +1,33 @@
# 003 gate checklist (handoff → 004)
**Date:** 2026-09-14
**Device:** LAB `11231JEC207477` only (GOLDEN untouched)
## Naming (owner clarification)
- **LAB target ROM:** Android **16** / LineageOS **23.2**
- **GOLDEN:** AXP Pro on Android **15** — reference/read only
- Do **not** treat “A15” as a synonym for Android 16
## Build / flash
| Item | Status | Evidence |
|------|--------|----------|
| Vanilla LOS build | pass | `#### build completed successfully` / `exit=0` 2026-09-14T13:52:46Z |
| Artifacts + SHA-256 + manifest -r | pass | `/srv/rom/artifacts/003-vanilla/20260914-lineage-23.2-sunfish/` + laptop `.local-artifacts/…` |
| Unlock + flash LAB | pass | `unlocked: yes`; sideload `Total xfer: 1.00x` |
| Identity | pass | `ro.lineage.version=23.2-20260914-UNOFFICIAL-sunfish`; no iodé/AXP props |
| SELinux / encrypt | pass | Enforcing; `encrypted` |
| P1 hardware smoke | pass | adb + owner confirmation of main functions 2026-09-14 |
## Notes (non-blocking)
- Public **test-keys** banner — expected until `011`
- `verifiedbootstate=orange` / unlocked — expected
- Vendor security patch **2023-09-05** — EOL Pixel; document further in `013`
- Titan M `libnos_datagram` during recovery wipe — ignore; install status 0
- Platform ASB for this baseline: trust Lineage merges (`ro.build.version.security_patch=2026-09-01`); no separate CVE audit in `003`
## Gate
**`003` complete → proceed to `004` (microG + GSF Proxy + patched Phonesky) on LAB only.**
+67
View File
@@ -0,0 +1,67 @@
# 003 — LAB smoke checklist (vanilla LOS 23.2)
Device serial: `11231JEC207477`
Build: `lineage-23.2-20260914-UNOFFICIAL-sunfish`
Zip SHA-256: `f89edfe1fba7062eb8f8e3cea5012f209fefe587bf447cf4f83a7017812e5852`
Date: 2026-09-14
Status: `pass` / `fail` / `skip` + short evidence.
## Identity (adb 2026-09-14)
| Prop | Value |
|------|--------|
| `ro.lineage.version` | `23.2-20260914-UNOFFICIAL-sunfish` |
| `ro.build.flavor` | `lineage_sunfish-userdebug` |
| `ro.build.display.id` | `BP4A.251205.006 test-keys` |
| Slot | `_a` (install was on `b`; post-update active `_a`) |
| Prior ROM props | none (no iodé / AXP / microG) |
## P1
| Item | Status | Notes |
|------|--------|-------|
| Boots to UI | pass | owner + adb up ~7 min |
| Encryption / PIN | pass | `ro.crypto.state=encrypted` (set PIN in setup) |
| SELinux Enforcing | pass | `getenforce` → Enforcing |
| Android security patch | pass | `2026-09-01` |
| Vendor security patch / baseline | pass | `2023-09-05` (EOL Pixel vendor train — expected) |
| SIM detection | pass | `gsm.sim.state=LOADED`, operator Movistar |
| Mobile data | pass | owner 2026-09-14 — main functions OK |
| Phone calls | pass | owner 2026-09-14 |
| SMS | pass | owner 2026-09-14 |
| Wi-Fi | pass | connected `Pixel_1216` |
| Bluetooth | pass | owner 2026-09-14 |
| GPS | pass | owner 2026-09-14 |
| Camera(s) | pass | owner 2026-09-14 |
| Audio (speaker/mic) | pass | owner 2026-09-14 |
| NFC | pass | owner 2026-09-14 |
| Sensors (basic) | pass | accel/gyro/mag/light/prox listed + active |
| Fingerprint | pass | owner 2026-09-14 |
| USB data / ADB | pass | adb device authorized |
| Charging | pass | USB powered, level 99 |
## Extended
| Item | Status | Notes |
|------|--------|-------|
| Airplane mode | skip | not run |
| Flashlight | _user_ | |
| Vibration | _user_ | |
| Screen rotation | _user_ | |
| USB OTG | skip | |
| Headphone jack | n/a | sunfish has none |
| WebView basic page | pass | `com.android.webview` present (`/product/app/webview`) |
| Sideload non-Google APK | skip | |
| Widevine / DRM level | skip | check later if needed |
| eSIM state | skip | |
| WireGuard kernel baseline | skip | |
| Bootloader version | pass | `s5-0.5-10252351` |
| Baseband / modem | pass | `g7150-00112-230505-B-10075601` |
| AVB / lock state | note | `verifiedbootstate=orange`, `flash.locked=0` (unlocked — expected) |
| Public test-keys banner | note | expected for UNOFFICIAL userdebug until `011` |
## Gate to `004`
P1 blockers unresolved? **no** — owner confirmed main functions 2026-09-14.
Handoff: `docs/003-gate-handoff.md`. Proceed to `004`.
+105
View File
@@ -0,0 +1,105 @@
# 004 / 005 — LAB microG Self-Check
Device: LAB `11231JEC207477` (never GOLDEN)
Build: `23.2-20260915-UNOFFICIAL-sunfish`
Date: 2026-09-15
Evidence: `/srv/rom/artifacts/005-spoofing/20260915-us6-validation/LAB-US6.txt`
## Packages
| Package | `pm path` | Notes |
|---------|-----------|-------|
| `com.google.android.gms` | `/system_ext/app/GmsCore/GmsCore.apk` | PRESIGNED; from zip |
| `com.google.android.gsf` | `/system_ext/app/GsfProxy/GsfProxy.apk` | PRESIGNED; from zip |
| `com.android.vending` | system priv-app from zip; LAB currently `/data/app/…` after exact-alarm `adb install -r` | Next bacon restores `/system/priv-app/Phonesky/` |
| FakeStore | absent | `pm list packages` — no FakeStore |
## Spoofing (`005`)
| Check | Result |
|-------|--------|
| `settings get secure allow_signature_fake` | `1` |
| `FAKE_PACKAGE_SIGNATURE` user 0 gms + vending | `granted=true` |
| `FAKE_PACKAGE_SIGNATURE` user 10 (Island) gms + vending | `granted=true` |
| Self-Check correct signature (gms + Play) | pass (operator) |
| Magisk / KSU / `su` | absent |
## Self-Check
| Item | pass/fail | Notes |
|------|-----------|-------|
| Package / ROM signature rows | pass | after spoof grant |
| Permission rows incl. spoof | pass | Developer Options toggle + grant |
| Play companion / Phonesky sig | pass | project JKS allow-listed in `005` |
## Play Store
| Check | Result | Notes |
|-------|--------|-------|
| Play opens | pass | |
| Login | pass | no `UNREGISTERED_ON_API_CONSOLE` in logcat |
| Install app (owner + work) | pass | after exact-alarm Phonesky (`USE_EXACT_ALARM`) |
| Version | `52.9.22-24` | |
| In-app Play self-update | may fail | AXP-like; acceptable |
## Modern location (BeaconDB / Positon)
Mozilla Location Service backends are dead. Prefer an Ichnaea-compatible URL:
**BeaconDB** (`https://api.beacondb.net`) or **Positon** (`https://api.positon.xyz/?key=…`).
Do **not** reintroduce UnifiedNlp Déjà Vu / Nominatim backend APKs on 0.3.x.
1. Open **microG Settings → Location**.
2. Enable network / Wi‑Fi / cell location as shown in Self-Check.
3. Enable the in-GmsCore toggles that still exist in 0.3.x (e.g. remember from
GPS, request from online service / hotspot) — these are **not** the old
UnifiedNlp plugin list.
4. Set (or confirm) the service URL (BeaconDB or Positon).
5. Do **not** install UnifiedNlp backend APKs from F-Droid.
| Check | Result |
|-------|--------|
| BeaconDB / Positon URL documented | yes |
| UnifiedNlp backends required | no |
### LAB observation (follow-up — 2026-09-16) — **fixed (prebuilt)**
Root cause: staged GmsCore was official **`*-hw`** (`versionName=…-hw`). That
flavor omits `NetworkLocationService` /
`NetworkLocationProviderService` from the manifest, so Location UI hides
Wi‑Fi/cell NLP (only Timeline-style bits remain). **GOLDEN** / abu ship the
**default** flavor (`versionName` without `-hw`) and show the full Location
options.
**Fix (host staged 2026-09-16):** replace with
`com.google.android.gms-252432032.apk` → `versionName=0.3.16.252432` (non-hw),
synced to `vendor/sunfish-a16/prebuilt/GmsCore.apk`. Quick LAB check without
bacon: `adb install -r` the default APK (same microG signer), then:
```bash
adb shell dumpsys package com.google.android.gms | grep -iE 'NetworkLocationProvider|NetworkLocationService'
adb shell dumpsys package com.google.android.gms | grep versionName=
# expect versionName=0.3.16.252432 (no -hw) + both NetworkLocation* components
```
Permanent: next `bacon` picks up the new prebuilt (~12.5 MiB larger than hw).
Positon remains a valid URL if preferred over BeaconDB.
After `adb install -r` of the default APK, `dumpsys` may still list both
`versionName=0.3.16.252432` and `…-hw` until the next full image flash replaces
`/system_ext/app/GmsCore` (data overlay + old system base). NLP services already
come from the default update.
## microG F-Droid updates
| Check | Result | Notes |
|-------|--------|-------|
| Add [microG F-Droid repo](https://microg.org/fdroid/repo) | optional | one-time |
| F-Droid sees GmsCore update (when newer) | expected | official `PRESIGNED` on system_ext |
| Do **not** install Companion/FakeStore from F-Droid | — | conflicts with Phonesky |
## AXP layout sanity
| Check | Result | Notes |
|-------|--------|-------|
| Phonesky | priv-app in image | LAB data path only after hotfix upgrade |
| GmsCore / GsfProxy | `/system_ext/…` | matches AXP GOLDEN layout intent |
+65
View File
@@ -0,0 +1,65 @@
# 004 — Phonesky / microG runbook (rom-dev01)
Proprietary Google/Play APKs **never** go in Git. Binaries live on `rom-dev01`
under `/srv/rom/prebuilts/`.
## Owner policy (2026-09-15)
- **Copy AXP’s packaging scheme** (GOLDEN layout), modernize versions (LOS 23.2 /
A16, latest microG + patched latest Play).
- Root later = **KSU-Next + SusFS** (`007`), not Magisk — do not design `004`
around Magisk modules.
- Prefer AXP behaviour over ad-hoc shortcuts (no “everything on product”, no
ABI-strip that forces re-signing official microG).
## AXP GOLDEN layout (mirror this)
| Package | Partition | Privileged? |
|---------|-----------|-------------|
| Phonesky | `system` priv-app | yes |
| GmsCore | `system_ext` app | no (AXP: `MicrogGmsCore_GH_presigned`) |
| GsfProxy | `system_ext` app | no |
Host packaging: `vendor/sunfish-a16/{Android.mk,gms.mk,BoardConfig.mk}`.
## Layout on host
| Path | Contents |
|------|----------|
| `/srv/rom/prebuilts/microg/` | official `GmsCore.apk`, `GsfProxy.apk`, `VERSIONS.txt` |
| `/srv/rom/prebuilts/phonesky/` | patched `Phonesky.apk`, XMLs, `VERSIONS.txt`, `upstream/` |
| `/srv/rom/secrets/phonesky-signing/` | project JKS for Phonesky |
| `/srv/rom/tools/phonesky/` | AXP `packages_apps_phonesky` worktree + tools |
## Stage microG (latest official — F-Droid-updatable)
1. Download latest **default** `com.google.android.gms-NNNNNNN.apk` (no `-hw`) +
`GsfProxy.apk` from microG GitHub releases. Match GOLDEN: `versionName`
must **not** end in `-hw` (Huawei flavor drops NLP Location services /
UI). Example: `v0.3.16.252432` → `com.google.android.gms-252432032.apk`.
2. Keep **official signature** (`LOCAL_CERTIFICATE := PRESIGNED`). **Never**
re-sign / ABI-strip GmsCore for size — that breaks F-Droid updates.
3. Do **not** install microG’s `com.android.vending-*.apk` (FakeStore).
## Stage Phonesky (patch latest Play — AXP procedure)
1. Tools: apktool, aapt, zipalign, `apksigner` via `java -jar …/apksigner.jar`.
2. Project keystore once under `/srv/rom/secrets/phonesky-signing/`.
3. `apkmd` + AXP `apkmirror.json` → `/srv/rom/prebuilts/phonesky/upstream/`.
4. Checkout AXP `packages_apps_phonesky`; run `DEBUG=yes ANDROIDSDK=35 ./patch-playstore …`
(host adaptations: apktool 2.12 build without `-t`; **zipalign then sign**).
5. Stage fat patched APK as system priv-app + AXP `etc/` XMLs to **system**.
## Partition headroom
If `check_partition_sizes` fails after adding the AXP-sized set, raise
`BOARD_SUPER_PARTITION_ERROR_LIMIT` in `vendor/sunfish-a16/BoardConfig.mk`
(keep under `BOARD_GOOGLE_DYNAMIC_PARTITIONS_SIZE`). Do **not** “fix” by
destroying the microG signer.
## Signing (FR-010)
| Package | Signer | Updates |
|---------|--------|---------|
| GmsCore / GsfProxy | Official microG | F-Droid microG repo |
| Phonesky | Project JKS | Same-signer patched APKs |
+92
View File
@@ -0,0 +1,92 @@
# 005 — Restricted signature spoofing runbook
**Feature**: `005-restricted-signature-spoofing`
**LAB only**: serial `11231JEC207477` — never GOLDEN
**Host**: `rom-dev01` under `/srv/rom/`
## What we discovered (2026-09-15)
LineageOS 23.2 **already** ships restricted microG spoofing
(`frameworks/base` commit `23c0a5c19844`, LuK1337):
| Gate | Stock LOS behaviour |
|------|---------------------|
| Build | `userdebug`/`eng` only (`isDebuggable()` JNI) |
| Packages | `com.google.android.gms`, `com.android.vending` only |
| Signer | Must match **microG release** cert (`MICROG_REAL_SIGNATURE`) |
| Fake value | Must equal platform Google cert (`MICROG_FAKE_SIGNATURE`) |
| Surfaces | Spoofs both `PackageInfo.signatures` and `signingInfo` |
Gaps vs our stack / AXP UX:
1. **Our Phonesky** is signed with the **project JKS**, not microG — stock
gate rejected it → Play `UNREGISTERED_ON_API_CONSOLE`.
2. No `FAKE_PACKAGE_SIGNATURE` permission → microG Self-Check permission rows
empty.
3. No Developer Options master switch (`allow_signature_fake`).
## What we applied
Script (idempotent, marker `SUNFISH_A16_005_SIGSPOOF`):
- Meta: `vendor/sunfish-a16/patches/005-sigspoof/apply_005_sigspoof.py`
- Host copy: `/srv/rom/src/lineage/vendor/sunfish-a16/patches/005-sigspoof/`
- References: `/srv/rom/references/extendrom` @ `f129253c…`
- Cert hex: `/srv/rom/artifacts/005-spoofing/phonesky-cert.hex`
(SHA-256 `31:31:86:CB:…:BD:56`)
Changes on the LOS tree:
1. Extend `isMicrogSigned()` to accept **project Phonesky** signer for
`com.android.vending`.
2. Gate spoofing on `Settings.Secure.allow_signature_fake` (default **0**).
3. Require grant of `android.permission.FAKE_PACKAGE_SIGNATURE`.
4. Port EXTENDROM permission + Settings Developer Options toggle + strings.
5. Keep packaging leftovers: `LOCAL_REPLACE_PREBUILT_APK_INSTALLED` + Play FGS
privapp XML (already in `vendor/sunfish-a16/`).
**LAB 2026-09-15 post-flash:** Self-Check / spoofing / no
`UNREGISTERED_ON_API_CONSOLE` OK. Play install failures / ANR: `DownloadService`
/`VpaService` FGS `systemExempted` needs **any of** `SCHEDULE_EXACT_ALARM` /
`USE_EXACT_ALARM` / VPN — privapp alone is not enough if the APK does not
**request** those permissions.
**Fix (2026-09-15):** Phonesky rebuild adds `USE_EXACT_ALARM` +
`SCHEDULE_EXACT_ALARM` to the manifest; artifact
`/srv/rom/artifacts/005-spoofing/Phonesky_exact-alarm.apk`; pushed to LAB via
`adb install -r` (same project JKS); vendor prebuilt updated under
`/srv/rom/src/lineage/vendor/sunfish-a16/prebuilt/Phonesky.apk` for next bacon.
`USE_EXACT_ALARM: granted=true` on LAB after upgrade.
Re-apply after `repo sync` that resets frameworks/Settings:
```bash
python3 /srv/rom/src/lineage/vendor/sunfish-a16/patches/005-sigspoof/apply_005_sigspoof.py
```
Backups: `/srv/rom/artifacts/005-spoofing/bak-snapshots/` (never leave
`*.005.bak` under `res/` — aapt2 will fail).
## LAB validation (US6 — 2026-09-15)
Accepted on LAB `11231JEC207477` / build `23.2-20260915-UNOFFICIAL-sunfish`.
Evidence: `/srv/rom/artifacts/005-spoofing/20260915-us6-validation/LAB-US6.txt`.
1. ~~Flash LAB; `pm path` gms / gsf / vending~~ — pass
2. ~~Allow signature spoofing~~ — `allow_signature_fake=1`
3. ~~Self-Check correct signature~~ — pass (grant on user 0 + Island 10)
4. ~~Play sign-in / installs~~ — pass after exact-alarm Phonesky; no `UNREGISTERED_ON_API_CONSOLE`
5. Location: configure BeaconDB `https://api.beacondb.net` (no UnifiedNlp) — steps in
`docs/004-lab-selfcheck.md` + `quickstart.md`
```bash
adb -s 11231JEC207477 shell settings get secure allow_signature_fake
# expect 1 when enabled
```
**Handoff:** first stack done → next feature **`006` banking rootless**. Do **not**
add KernelSU/`007` until after `006`.
## Out of scope
- Banking apps (`006`), KSU/root (`007`), Magisk/LSPosed.
+80
View File
@@ -0,0 +1,80 @@
# abu (KernelSU-Next) vs ours (ReSukiSU) — SusFS integration checklist
> **Historical hypothesis checklist:** The [2026-09-16 source audit](007-susfs-source-audit.md)
> supersedes causal claims here. ReSukiSU intentionally supports SUSFS as an
> alternative inline hook provider; forcing MANUAL+SUSFS is a local porting
> choice, not an established universal fix. Check module ABI and command wire
> formats as well as Kconfig. The reference checkout is not proven identical
> to the owner's working Abu L22 binary.
Oracle: `/srv/rom/references/abu-msm-4.14-ksu` (`drivers/kernelsu` → `KernelSU-Next/kernel`).
Ours: ReSukiSU under `msm-4.14/drivers/kernelsu` + LOS 23.2 A16.
**Do not copy abu ROM/boot**; copy *model* only. Forks differ (Next ≠ ReSukiSU).
## Architectural model (abu — correct)
| Topic | abu KernelSU-Next | ReSukiSU upstream (broken for us) | Ours after patches |
|-------|-------------------|-------------------------------------|--------------------|
| SusFS | **Addon** `bool` under `menu "KernelSU - SUSFS"`, `depends on KSU` | Was a **choice** entry XOR with MANUAL/TRACEPOINT | Kconfig: SusFS outside choice (abu-like) |
| Hooks | `KSU_KPROBES_HOOK` default y; or manual (disable kprobes) | `choice` MANUAL / TRACEPOINT / (was SUSFS) | MANUAL_HOOK + AUTO_* for 4.14 |
| `susfs_init()` | `#ifdef CONFIG_KSU_SUSFS` always in `ksu.c`, **independent** of hook method | Often behind `#elif CONFIG_KSU_SUSFS` only | Fixed `init.c`: MANUAL branch also calls `susfs_init()` |
| Hook enable keys | N/A (different code) | `#elif SUSFS` before `#elif MANUAL` → no `ksu_init_rc_hook` when both | Fixed `ksud_integration.c`: SusFS-only branch if `!MANUAL_HOOK` |
## Failures we already hit (XOR leftovers)
1. Defconfig SusFS on → choice flipped → **no MANUAL_HOOK** → vibrator hang / broken hooks (`T004038Z`).
2. MANUAL+SusFS without **AUTO_*** → early logo reboot (`T091211Z`).
3. AUTO_* + SusFS → link error `ksu_init_rc_hook` (SUSFS `#elif` won).
4. Even after link fix: without `susfs_init()` in MANUAL path, SusFS would be dead code.
## Remaining `#elif CONFIG_KSU_SUSFS` in our tree (review)
| File | Pattern | Risk when MANUAL+SUSFS | Action |
|------|---------|------------------------|--------|
| `runtime/ksud_integration.c` | SusFS iff `!MANUAL` | OK | done |
| `core/init.c` | MANUAL then `ifdef SUSFS` init; elif SUSFS legacy | OK | done |
| `feature/sucompat.h` | `#elif SUSFS` before else MANUAL macros | Uses SusFS TIF/helpers — **intended** for addon | leave; verify LAB |
| `supercall/dispatch.c:527` | mark get: SUSFS then else | SusFS path when addon — OK | leave |
| `supercall/dispatch.c:902` | MANUAL before SUSFS for hook_type string | Reports "Manual" when both — OK | leave |
| `feature/sucompat.c` | `SUSFS \|\| MANUAL` | OK compose | leave |
**Rule:** any `#elif CONFIG_KSU_SUSFS` that **defines symbols or inits** needed by MANUAL/AUTO must become either `&& !MANUAL` or `#ifdef SUSFS` *nested inside* MANUAL (abu: always `#ifdef SUSFS`).
## Feature flag delta (Kconfig)
abu Next has extras we lack (Next-specific / different SusFS port):
- `KSU_SUSFS_HAS_MAGIC_MOUNT`
- `KSU_SUSFS_AUTO_ADD_SUS_*` / `TRY_UMOUNT*`
- `KSU_SUSFS_SUS_SU` (needs KPROBES)
Our ReSukiSU SusFS set: PATH/MOUNT/KSTAT/LOG/HIDE_SYMBOLS/SPOOF_CMDLINE/OPEN_REDIRECT/SUS_MAP (+ SPOOF_UNAME optional).
Bring-up: keep **SPOOF_UNAME off** (already commented in defconfig); consider SPOOF_CMDLINE off until boot stable.
abu `sunfish_defconfig` in this reference has **0** `CONFIG_KSU*` lines — enablement is elsewhere / not this file; do not treat that defconfig as SusFS pin list.
## Hook stack (do not mix blindly)
| | abu | ours |
|--|-----|------|
| Primary | KPROBES (`CONFIG_KSU_KPROBES_HOOK`) | MANUAL + AUTO_SETUID/INITRC/INPUT |
| Non-GKI docs | kernelsu.org manual integrate | resukisu.org manual integrate |
Porting abu **kprobe** assumptions into ReSukiSU MANUAL builds = future footgun.
## Pre-build gate (mandatory)
Before each SusFS `bootimage`:
```bash
CFG=out/target/product/sunfish/obj/KERNEL_OBJ/.config
grep -E "CONFIG_KSU_MANUAL_HOOK=y|CONFIG_KSU_MANUAL_HOOK_AUTO_.*=y|CONFIG_KSU_SUSFS=y" "$CFG"
# must have MANUAL=y, all three AUTO=y, SUSFS=y
# must NOT have "# CONFIG_KSU_MANUAL_HOOK is not set"
nm …/ksud_integration.o | grep ksu_init_rc_hook # after CC
```
After Image: extract-ikconfig and confirm same.
## Flash discipline
Flash SusFS to one slot only; keep penúltimo on the other; `--set-active`; both-slot rollback after failure.
+60
View File
@@ -0,0 +1,60 @@
# Debug boot — freeze on panic (photo / dump)
Normal sunfish kernel: `CONFIG_PANIC_TIMEOUT=5` + often
`CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC` → reboot in seconds. Console is
**UART only** (`console=ttyMSM0`); `# CONFIG_VT is not set` → **no Oops text
on the phone display** (you only see the Google logo loop).
## What a “debug boot” must include (owner)
For a useful photo, **both** are required — freeze alone is not enough:
1. **Freeze:** `CONFIG_PANIC_TIMEOUT=0` + cmdline `panic=0`; disable
`CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC` so it does not reboot in 5s.
2. **On-screen console:** `CONFIG_VT` + `CONFIG_FRAMEBUFFER_CONSOLE` (+
`console=tty0` in cmdline). Without this, you only see the Google logo.
| Goal | Mechanism | Photo-able? |
|------|-----------|-------------|
| Stop reboot loop | `panic=0` / `PANIC_TIMEOUT=0`, no wdog-bite-on-panic | Logo freezes only |
| Oops on screen | VT + FRAMEBUFFER_CONSOLE + `console=tty0` | Yes if panic after FB; early may still be logo |
| Dump after hang | Freeze + penúltimo + immediate `klog`/`logfs` dd | Not a photo |
Use **only on LAB**, SusFS/debug trials. Never ship as daily boot.
Next SusFS debug build after the sigfix trial must enable **both**.
## Host recipe (LAB SusFS debug boot)
1. Defconfig fragment (or temporary edits) on `rom-dev01`:
```text
CONFIG_PANIC_TIMEOUT=0
# CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC is not set
# optional on-screen console (experimental on Pixel 4.14):
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
```
2. Cmdline (device tree or unpack/repack boot): add `panic=0` and optionally
`console=tty0`. Prefer a **vendor_sunfish / BoardConfig** override gated by
a make var (e.g. `SUNFISH_KERNEL_PANIC_DEBUG=1`) so normal bacon stays clean.
3. Build `bootimage`, name artifact
`boot-resukisu-susfs-DEBUG-YYYYMMDDTHHMMSSZ.img`.
4. Flash only to `boot_b`; keep penúltimo on `boot_a`. On hang: photo if text
appears; Vol−+Power → `--set-active=a`.
## Capture order (always)
1. Panic / hang on SusFS (debug) slot
2. Fastboot **before** Android success on good slot
3. Then penúltimo + immediate `dd` of `klog`/`logfs` (4a has no `fastboot fetch`)
## Status
Documented 2026-09-16 after SusFS early `kernel_panic` with empty Oops in klog.
Sigfix rebuild may already address root cause; keep this recipe for the next
opaque bootloop.
+198
View File
@@ -0,0 +1,198 @@
# 007 — KernelSU-Next on sunfish (LAB runbook)
**LAB only**: serial `11231JEC207477` — never GOLDEN `11071JEC203839`
**Host**: `rom-dev01` — artifacts `/srv/rom/artifacts/007-kernelsu/`, logs
`/srv/rom/logs/007-build/`
**Managed profile**: **Island** (current LAB/GOLDEN vehicle; historical
“Insular” docs are workflow reference only)
## Latest LAB results
**2026-09-16:** SusFS DEBUG bring-up **PASS** (matched vendor); banks still
**FAIL** (Talsec `-69` / root lock). Full write-up:
[`docs/007-lab-results-20260916.md`](007-lab-results-20260916.md).
## Hard-gates
| Gate | Rule |
|------|------|
| Yuh (`com.yuh`) | **PASS** usable session in Island on the tested stack |
| Swissquote (`com.swissquote.android`) | **PASS** usable session in Island on the **same** stack |
| Root | Banks **never** receive root; hide/umount **verified** (not toggle-only) |
| Magisk | Not production |
If either bank fails after SusFS/attestation experiments → **cancel root**
(Phase 9): keep incomplete / withdraw KSU; pivot to native `008`/`009` + optional
stock phone. Do **not** ship “root without both banks”.
## Sequencing (constitution 0.2.1)
- `006` rootless PASS gate is **deferred**.
- Before first rooted flash: **T039** rootless snapshot of *this* LAB ROM
(`pre-root/`). FAIL allowed; record facts.
- Then KSU → verified hide → SusFS → attestation → both-bank PASS.
- **Pins:** always refresh to newest compatible ReSukiSU/manager (non-shallow);
record tag + `KSU_VERSION` before `mka`. Never rebuild from a stale host copy.
## `002` KeyMint / Yuh evidence (index)
| Source | What | Path / doc |
|--------|------|------------|
| GOLDEN AXP+Magisk | Yuh FAIL — HA / “certificate invalid”; KeyMint `HARDWARE_TYPE_UNAVAILABLE` | `docs/002-golden-inventory.md` §Compatibility; host refs under `/srv/rom/references/axp/golden/…` when present |
| LAB iodé A16 (no Magisk, BL locked) | Same KeyMint/RKP class on Yuh open | `docs/002-lab-iodes-baseline.md`; host `…/lab/2026-09-13-iodes-baseline/yuh-…` |
| Contracts | Hide + both-bank gate | `specs/007-kernelsu-sunfish/contracts/` |
## Manager UI is **not** in the ROM zip
Kernel root (KSU-Next / ReSukiSU) does **not** ship a visible Settings tile.
After flash: install the matching **spoofed manager APK** (version ≤ kernel
`KSU_VERSION`), then modules (TEESimulator-RS / Zygisk / PIF, etc.). Same on
torukk’s MicroG zip — ResukiSU/KSU is kernel-only until the manager is installed
(see XDA #41; our pin: `.local-artifacts/007-kernelsu/ReSukiSU_*spoofed_*.apk`).
## Host policy
**All heavy work on `rom-dev01` only** (kernel clone, integrate, `mka`, image
staging). Laptop = thin client (adb/fastboot, Spec Kit, docs). Do not run kernel
builds or multi‑GB clones on the laptop.
**Long jobs must be detached on the host** — never leave `mka`/`ninja` on a
laptop SSH pipe (Wi‑Fi drops kill the build). Prefer **tmux** via
`/srv/rom/artifacts/bin/rom-detach` (falls back to screen / nohup):
```bash
/srv/rom/artifacts/bin/rom-detach 007-boot -- bash -lc \
'cd /srv/rom/src/lineage && source build/envsetup.sh && lunch lineage_sunfish-bp4a-userdebug && mka bootimage -j$(nproc)'
cat /srv/rom/artifacts/detach-007-boot.status
tmux ls # attach: tmux attach -t 007-boot
```
Agents: start detached, then only poll `detach-*.status` / logs with short SSH.
| Path on host | Role |
|--------------|------|
| `/srv/rom/src/lineage/kernel/google/msm-4.14` | Our LOS kernel (integrate here) |
| `/srv/rom/src/lineage/kernel/google/msm-4.14/KernelSU-Next` | setup.sh checkout @ `a54e4fa…` |
| `/srv/rom/references/kernelsu-next/` | KernelSU-Next `legacy` mirror (setup source) |
| `/srv/rom/references/abu-msm-4.14-ksu/` | abu-adm `ksu-susfs-23.0` **reference only** |
| `/srv/rom/artifacts/007-kernelsu/abu-reference/` | Owner-supplied abu `boot-abu.img` + `dtbo-abu.img` + SHA256 |
| `/srv/rom/artifacts/007-kernelsu/` | Our outs / evidence |
| `/srv/rom/logs/007-build/` | Build logs |
### Integration status (2026-09-16)
- **Production root fork for LAB:** **ReSukiSU** (symlink `drivers/kernelsu` →
`/srv/rom/references/resukisu/kernel`, full non-shallow clone). KernelSU-Next
remains historical learning only.
- Manual hooks (`CONFIG_KSU_MANUAL_HOOK`): `fs/open.c`, `fs/exec.c`, `fs/stat.c`,
`fs/read_write.c`, `drivers/input/input.c`, `kernel/sys.c` (`setresuid`),
`kernel/reboot.c`
- `sunfish_defconfig`: `CONFIG_KSU=y`, `CONFIG_KSU_MANUAL_HOOK=y`, all three
`AUTO_*` hooks **on** for 4.14, SusFS options as addon (not XOR). See
`docs/007-abu-vs-ours-susfs.md`.
- **Our boots (use these — not Downloads):**
| Role | Artifact |
|------|----------|
| Penúltimo (ReSukiSU, no SusFS) | `boot-resukisu-our-20260915T183729Z.img` / `boot-resukisu-our-latest.img` |
| Último (ReSukiSU + SusFS) | `boot-resukisu-susfs-20260916T004038Z.img` / `…-susfs-latest.img` (sha `455a77a6…`) |
- SusFS: **built** (API bridge 4.14 ↔ ReSukiSU ioctl). **Not claimed** on LAB —
trial only; US2 hide not finished before build. See
`docs/session-20260916-handoff.md`.
## Community / abu — **reference archive only**
Same problem space (sunfish + microG + hide + banks). **Not** our flash path.
Do not install abu/torukk ROMs or `~/Downloads/boot-sunfish-resukisu*.img` as
“our” kernel.
| Topic | Useful reference | Our action |
|-------|------------------|------------|
| Root fork | ReSukiSU for hard banks (XDA) | We already build **our** ReSukiSU boot |
| Attestation | TEESimulator-RS | Prefer over TrickyStore when modules resume |
| Modules | Zygisk Next + PIF inject | Documented exception if bank gate needs it |
| SusFS / kernel | abu `ksu-susfs` trees, `abu-reference/` imgs | Diff / port ideas only |
| Their full ROM | torukk 22.2 MicroG / abu A15 | **Ignore for day-to-day** — restore always = our 23.2 bacon |
Host refs: `/srv/rom/references/abu-msm-4.14-ksu/`,
`/srv/rom/artifacts/007-kernelsu/abu-reference/` (`boot-abu` / `dtbo-abu`).
```bash
# LAB — our penúltimo (preferred after ROM sideload)
# Always flash BOTH slots after a failed SusFS/bootloop trial — bootloader may
# mark the other slot unbootable and leave current-slot on the bad one.
fastboot flash boot_a .local-artifacts/007-kernelsu/boot-resukisu-our-20260915T183729Z.img
fastboot flash boot_b .local-artifacts/007-kernelsu/boot-resukisu-our-20260915T183729Z.img
fastboot --set-active=a # or the slot that last ran our 23.2 bacon successfully
fastboot reboot
# host: /srv/rom/artifacts/007-kernelsu/boot-resukisu-our-20260915T183729Z.img
# Emergency (no KSU): bacon stock boot
# fastboot flash boot_a|boot_b .local-artifacts/007-kernelsu/boot-005-known-good.img
```
**Slot lesson (2026-09-16):** SusFS bootloop left `current-slot:b` and
`slot-unbootable:a:yes`. Flashing only `boot` (→ active slot) could not revive
LAB; `boot-005` on **both** slots + `--set-active=a` recovered. Before any
kernel trial: `fastboot getvar current-slot` / `slot-unbootable:*`. After
failure: flash rollback to **boot_a and boot_b**, then set the known-good slot.
## Pin table (2026-09-16)
| Component | Revision | Status | Notes |
|-----------|----------|--------|-------|
| ReSukiSU | host `/srv/rom/references/resukisu` (non-shallow; match manager) | **active** | record tag + `KSU_VERSION` before each `mka` |
| KernelSU-Next | `a54e4fa…` (`legacy`) | archived | learning only |
| LOS sunfish kernel | `28f9290ae067…` + dirty SusFS/hooks | tree | `msm-4.14` |
| SusFS | bridged in-tree (trial) | **rebuild** | `T004038Z` hung (no MANUAL_HOOK); `T091211Z` logo-reboot (MANUAL+SUSFS but **AUTO_* hooks off**); next: AUTO_*=y + SusFS |
| Attestation stack | — | pending | TEESimulator-RS first candidate |
## Kernel inventory (T005)
| Item | Value |
|------|--------|
| Source | `/srv/rom/src/lineage/kernel/google/msm-4.14` |
| Device makefile | `device/google/sunfish/BoardConfigLineage.mk` → `TARGET_KERNEL_SOURCE := kernel/google/msm-4.14` |
| Defconfig | `arch/arm64/configs/sunfish_defconfig` |
| Boot image name | `Image.lz4` |
## KernelSU-Next choice (T006–T007) — amended by community 2026-08
**Earlier decision**: KernelSU-Next `legacy` + manual hooks for first in-tree POC.
**Amended (owner + torukk XDA 2026-08-22)**: For **hard-bank** detection
(Revolut-class; our gate is Yuh/Swissquote), community evidence favors
**ReSukiSU** + **TEESimulator-RS** (+ Zygisk Next + PIF inject) over Next+TrickyStore.
Keep Next work as learning / optional A/B; **do not** treat Next-only as the
banking solution if ResukiSU PoC is available.
**LAB PoC order (current, 2026-09-16):**
1. ~~Foreign ResukiSU / abu boots~~ — reference only; wrong for our A16 tree
2. **Done**: our ReSukiSU → `boot-resukisu-our-20260915T183729Z.img` (penúltimo)
3. **Done (build)**: SusFS trial → `boot-resukisu-susfs-20260916T004038Z.img` —
flash/claim **after** US2; prefer penúltimo on LAB until then
4. Manager + TEESimulator-RS ± Zygisk/PIF → bank gate
## Integration notes (T008)
Work on host tree; flash only our `boot-resukisu-our-*` / `boot-resukisu-susfs-*`
artifacts (never `~/Downloads/boot-sunfish-resukisu*`):
1. Wire ReSukiSU as **`drivers/kernelsu` → `/srv/rom/references/resukisu/kernel`** (full git clone lives *outside* the msm tree). Do **not** keep a `KernelSU/` directory inside `msm-4.14`: this tree’s `scripts/setlocalversion` walks nested `.git` dirs and appends `KernelSU-gHASH` into `uname -r` (easy bank detection).
2. Always use a **non-shallow** ReSukiSU clone so `KSU_VERSION = 30000 + rev-list + 700` matches the manager (≥35080). Shallow depth=1 produced bogus **30701**.
3. Enable `CONFIG_KSU=y` + `CONFIG_KSU_MANUAL_HOOK` (+ AUTO_* on 4.14).
4. Patch syscall hook sites per https://resukisu.org/guide/manual-integrate.html.
5. Build `mka bootimage`; verify `uname -r` has **no** `KernelSU` string before bank testing.
6. SusFS: built as trial; **do not claim** until US1–US2 green and T019 boots clean.
References: ReSukiSU manual integrate; abu/XDA only as SusFS/module oracle
(`docs/reference-sources.md` §8; `docs/session-20260916-handoff.md`).
## Rootless snapshot (T039)
See `/srv/rom/artifacts/007-kernelsu/pre-root/OUTCOMES.txt` and `preflight.txt`.
Recorded on `23.2-20260915-UNOFFICIAL-sunfish`: microG + Phonesky + spoof ON;
Island user 10 present; **no su**. Yuh/Swissquote **not installed** at snapshot
time (N/A outcomes). Reinstall via Aurora→Island before US5 banking gate.
+220
View File
@@ -0,0 +1,220 @@
# 007 LAB results — 2026-09-16
LAB only: serial `11231JEC207477`. GOLDEN not used.
Build base: `23.2-20260915-UNOFFICIAL-sunfish`.
Kernel under test: ReSukiSU **35153** + SusFS **v1.5.5**, DEBUG boot
`#8 … Wed Sep 16 12:09:18 UTC 2026`
(`boot-resukisu-susfs-DEBUG-20260916T115115Z.img`).
Related: [source audit](007-susfs-source-audit.md),
[attempts brief](007-susfs-attempts-chatgpt-brief.md),
[runbook](007-kernelsu-runbook.md).
## Executive summary
| Area | Result |
|------|--------|
| SusFS + ReSukiSU **boot** (matched vendor) | **PASS** |
| Manager 35153 + `susfs_init` | **PASS** |
| Play downloads (system Phonesky alone) | **FAIL** → fixed with exact-alarm APK hotfix |
| TEESimulator-RS + ReZygisk + PIF inject | Installed and running |
| Yuh / Swissquote usable session | **FAIL** (Talsec root detect / exit **-69**) |
| SusFS as banking hide by itself | **Not sufficient** (expected; US2/attestation still open) |
**Bottom line:** The SusFS bring-up *pipeline* works end-to-end (build →
matched `boot`+`vendor` → modules load → Manager sees SusFS). Banking PASS is
still blocked by root/environment detection; SusFS did not clear that gate.
---
## 1. Boot / SusFS bring-up
### What failed earlier (corrected causality)
1. **Boot-only SusFS flashes** left LAB on an old `/vendor/lib/modules` set.
With `CONFIG_MODVERSIONS=y`, `drv2624` (and peers) disagreed on CRCs
(**31/56** incl. `module_layout`). That matches the T004038Z-class hang in
`VibratorManagerService` when the module never loads — **not** proof of the
later early-panic series.
2. First matched-module attempt used a **hand-repacked** `vendor.img` with
wrong SELinux labels (`vendor_file` instead of
`vendor_kernel_modules`). `init-insmod-sh` got `avc: denied { module_load }`
→ empty `lsmod` → same vibrator / bootanim stall. **ADB was alive.**
Reproduce ABI check:
```bash
python3 scripts/check-module-abi.py <Module.symvers> <module.ko>
```
### What worked
| Step | Detail |
|------|--------|
| DEBUG boot | `panic=0`, VT/fbcon; artifact `.local-artifacts/007-kernelsu/boot-resukisu-susfs-DEBUG-20260916T115115Z.img` |
| Vendor | Repacked modules from same build; flash via **fastbootd** (`vendor_a` sparse); vbmeta `--disable-verity --disable-verification` |
| SELinux fix | Relabel `.ko` → `u:object_r:vendor_kernel_modules:s0` (`incrementalfs` → `vendor_incremental_module`) |
| Post-boot | `sys.boot_completed=1`, `drv2624` + ~40 modules loaded |
| SusFS | `dmesg`: `susfs_init … version: v1.5.5` |
| Manager | Spoofed `weogax.atbfxb.skiggr` **35153**; UI: driver 35153, SusFS v1.5.5, hook **Manual**, SELinux Enforcing |
`su` from adb: `uid=0` `u:r:ksu:s0`. No `/sys/fs/susfs` (normal for 1.5.5;
ioctl/supercall path).
### Architecture note (open)
MANUAL_HOOK + `CONFIG_KSU_SUSFS` remains a **local hybrid** vs upstream ReSukiSU
hook `choice`. This DEBUG build **boots** with that hybrid + matched vendor.
ChatGPT’s “hook ABI collision” theory is **not** required to explain the
vibrator hang; it remains a hypothesis for earlier logo→reboot trials that
were not re-run with matched vendor. Prefer a controlled **SusFS-inline,
no MANUAL**, min-features experiment later — do not treat hybrid as final
architecture.
Prepared but not on this boot: execve post-hook UAF patch
`vendor/sunfish/patches/007-kernelsu/0001-exec-avoid-post-hook-filename-uaf.patch`
(real bug; also present on root-only path → not SusFS-only panic proof).
---
## 2. Play Store “pending”
**Cause:** `DownloadService` crash loop:
```text
SecurityException: FGS type systemExempted requires
FOREGROUND_SERVICE_SYSTEM_EXEMPTED + (SCHEDULE_EXACT_ALARM | USE_EXACT_ALARM | VPN)
```
System `/system/priv-app/Phonesky` lacked `USE_EXACT_ALARM` in the **APK
manifest** (privapp XML alone is insufficient). Documented hotfix:
| Item | Path |
|------|------|
| Host | `/srv/rom/artifacts/005-spoofing/Phonesky_exact-alarm.apk` |
| Laptop | `.local-artifacts/004-microg/Phonesky_exact-alarm.apk` |
```bash
adb install -r .local-artifacts/004-microg/Phonesky_exact-alarm.apk
```
Not in the bacon image yet (deferred with other API warnings). Next bacon must
ship the exact-alarm Phonesky prebuilt.
---
## 3. Modules reinstall
Installed with `su -M` (global mount ns; plain `su` often cannot see
`/data/adb` under SusFS) via `/data/adb/ksud module install …`:
| Module | id | Version |
|--------|-----|---------|
| ReZygisk | `rezygisk` | v1.0.0 (515) |
| TEESimulator-RS | `tricky_store` | v6.0.1-307 |
| Play Integrity Fix [INJECT] | `playintegrityfix` | v4.7-1-inject-s |
Zips staged under `.local-artifacts/007-kernelsu/modules/`. Versions matched
upstream `update.json` at install time. Reboot required after install; user
confirmed activated.
**TEESimulator targets:** default `target.txt` had Play/GMS + test apps only.
`com.yuh` and `com.swissquote.android` must be listed (GUI may not expose this;
edit `/data/adb/tricky_store/target.txt` or module action). Adding them was
necessary for TEEsim to bind those UIDs; it did **not** by itself clear Talsec.
Observed TEEsim noise: `StrongBox op limit reached (hw=4 sw=0 max=4)`,
`Skip patching short certificate chain of length null`.
**PIF note:** `pif.prop` spoofed **Pixel 6** (`oriole`) on sunfish — device
mismatch risk for banks; revisit fingerprint before next bank trial.
---
## 4. Banking (Yuh / Swissquote)
### Observed behaviour
- Both load **Talsec** `libts.so`.
- Swissquote (and Yuh after retries): `VM exiting with result code **-69**`
then process death — intentional anti-root exit, **not** an uncaught Java
crash. Same signature as 2026-09-15 captures (with/without TEEsim).
- Probes in logcat: `getenforce`, `/proc`, serial props, `qemu.*`, `ps` /
`pid_max`, cache symlink, etc.
- Operator report: after many attempts Yuh **locked the app** citing **root**.
### Profile / policy gaps (spec US2 still open)
- Contract: banks **never** receive root; umount/hide must be **verified**.
- `.allowlist` contained both bank packages (binary profile); confirm in
Manager UI: **Deny / Forbidden** + **Umount modules** for
`com.yuh` and `com.swissquote.android` (all userIds tested).
- SusFS initialised ≠ verified path hide for banking UIDs.
- Island / work profile not re-validated in this session.
### Verdict on SusFS vs banks
SusFS **does not** replace: deny-root profiles, verified umount, coherent
attestation (TEEsim keybox/StrongBox), and PIF props matching the device.
Pipeline works; **both-bank PASS remains FAIL**.
---
## 5. Operational recipes (LAB)
### Flash matched DEBUG (fastbootd)
```bash
adb reboot fastboot # userspace fastbootd
fastboot flash vendor_a .local-artifacts/007-kernelsu/vendor-DEBUG-modules.sparse.img
fastboot --disable-verity --disable-verification flash vbmeta_a .local-artifacts/007-kernelsu/vbmeta.img
fastboot flash boot_a .local-artifacts/007-kernelsu/boot-resukisu-susfs-DEBUG-20260916T115115Z.img
fastboot flash boot_b .local-artifacts/007-kernelsu/boot-resukisu-susfs-DEBUG-20260916T115115Z.img
fastboot reboot
```
Rollback vendor (host): `/srv/rom/artifacts/007-kernelsu/vendor-pre-debug-20260915.img`.
### Module install (after SusFS boot)
```bash
adb push <zip> /data/local/tmp/ksu-mods/
adb shell 'su -M -c "/data/adb/ksud module install /data/local/tmp/ksu-mods/<zip>"'
adb reboot
```
### TEESimulator bank targets
```bash
adb shell 'su -M -c "grep -E \"yuh|swissquote\" /data/adb/tricky_store/target.txt ||
printf \"%s\\n\" com.yuh com.swissquote.android >> /data/adb/tricky_store/target.txt"'
```
---
## 6. Artifacts index
| Artifact | Location |
|----------|----------|
| DEBUG boot | `.local-artifacts/007-kernelsu/boot-resukisu-susfs-DEBUG-20260916T115115Z.img` |
| Vendor sparse (correct SELinux) | `.local-artifacts/007-kernelsu/vendor-DEBUG-modules.sparse.img` (+ host `…/vendor-repack/`) |
| Experiment dir | `a16-build:/srv/rom/artifacts/007-kernelsu/debug-20260916T115115Z/` |
| Module zips | `.local-artifacts/007-kernelsu/modules/` |
| Phonesky exact-alarm | `.local-artifacts/004-microg/Phonesky_exact-alarm.apk` |
| ABI checker | `scripts/check-module-abi.py` |
| RCA CRCs | `.local-artifacts/007-kernelsu/rca-20260916/` |
| Manager pin | `.local-artifacts/007-kernelsu/SPOOFED_MANAGER_PIN.txt` (35153) |
---
## 7. Open follow-ups (non-ordering commitment)
1. Bacon: ship exact-alarm Phonesky + always package `boot` with matching
vendor modules (and correct `vendor_kernel_modules` labels).
2. Confirm Manager Deny + Umount for both banks; prove with `su` from bank UID
and mount evidence (US2).
3. TEEsim StrongBox limit / keybox quality; PIF fingerprint → sunfish-coherent.
4. Optional control: SusFS-inline without MANUAL, min features, matched vendor.
5. Apply execve UAF patch on next kernel rebuild.
6. Do **not** claim banking PASS until Yuh + Swissquote usable sessions on the
same stack (runbook hard-gate).
+312
View File
@@ -0,0 +1,312 @@
# SusFS + ReSukiSU on sunfish LOS 23.2 — full attempt log (for external review)
> **Audit correction (2026-09-16):** See [source and artifact audit](007-susfs-source-audit.md).
> The claims below that missing MANUAL_HOOK caused the vibrator hang, or that
> the upstream hook choice is intrinsically broken, are hypotheses, not proven
> causes. The audit confirms incompatible installed/vendor module CRCs, an
> exec post-hook use-after-free, and incomplete/mismatched SUSFS API wrappers.
> The exact early-panic cause still requires a trace or controlled experiment.
**Date compiled:** 2026-09-16
**Project:** `sunfish-a16-rom` (independent; not SSYSTEMS)
**Device (LAB only):** Pixel 4a `sunfish`, serial `11231JEC207477`
**ROM base:** LineageOS **23.2** / Android **16** (`lineage-23.2-20260915-UNOFFICIAL-sunfish`)
**Goal:** ReSukiSU root **plus** SusFS hide addon on **our** `msm-4.14` tree (A16), using abu/GOLDEN as **behavioural oracles**, not as flash images.
**Ask for help:** Why do MANUAL_HOOK + AUTO_* + CONFIG_KSU_SUSFS builds still `kernel_panic` before userspace on this tree, while (1) penúltimo ReSukiSU-without-SusFS boots, and (2) an earlier SusFS-without-MANUAL build reached Android (then hung on vibrator)? What remaining XOR / composition bugs exist in ReSukiSU when treating SusFS as an addon like KernelSU-Next?
---
## 1. Concepts (do not confuse)
| Name | What it is |
|------|------------|
| **KernelSU (KSU)** | Generic root-in-kernel project family |
| **ReSukiSU** | Fork we ship (kernel driver + Manager). Version code **35153**, `v4.2.0-rc2-…@ReSukiSU` |
| **KernelSU-Next** | Different fork; what **abu** wires in their reference tree |
| **SusFS** | Kernel **addon** (simonpunk) for path/mount/uname/cmdline hide. **Not** a replacement for ReSukiSU. Should sit **on top** of a KSU-family driver |
**Intended product stack:**
```
apps / banks
↑
SusFS ← optional hide (compile into boot)
↑
ReSukiSU ← root
↑
msm-4.14 + LOS 23.2 system
```
**abu product habit:** two boots (root-only vs root+SusFS). Same idea: SusFS on/off at **compile time** in the boot image, not a runtime “activate SusFS” without the code in the kernel.
---
## 2. Important paths (host `rom-dev01` + laptop)
### Host
| Path | Role |
|------|------|
| `/srv/rom/src/lineage/` | LOS 23.2 tree |
| `/srv/rom/src/lineage/kernel/google/msm-4.14/` | Our kernel (dirty: ReSukiSU + SusFS + patches) |
| `/srv/rom/src/lineage/kernel/google/msm-4.14/drivers/kernelsu/` | ReSukiSU kernel module sources (in-tree) |
| `/srv/rom/src/lineage/kernel/google/msm-4.14/fs/susfs.c` | SusFS implementation |
| `/srv/rom/src/lineage/kernel/google/msm-4.14/arch/arm64/configs/sunfish_defconfig` | Defconfig (KSU/SusFS/debug knobs) |
| `/srv/rom/src/lineage/device/google/sunfish/BoardConfig-common.mk` | `BOARD_KERNEL_CMDLINE`; optional `SUNFISH_DEBUG_PANIC_SCREEN=1` → `panic=0 console=tty0` |
| `/srv/rom/artifacts/007-kernelsu/` | Boot images + managers + RCA |
| `/srv/rom/artifacts/007-kernelsu/susfs-fail-rca/` | ikconfig diffs, notes, abu checklist |
| `/srv/rom/artifacts/007-kernelsu/run-susfs-bootimage.sh` | Normal SusFS bootimage builder |
| `/srv/rom/artifacts/007-kernelsu/run-susfs-bootimage-debug.sh` | DEBUG builder (`SUNFISH_DEBUG_PANIC_SCREEN=1`) |
| `/srv/rom/logs/007-build/susfs-bootimage.log` | Cumulative build log (script tee) |
| `/srv/rom/logs/007-build/susfs-bootimage-debug.log` | Debug build log |
| `/srv/rom/artifacts/detach-*.log` | Per-session `rom-detach` logs |
| `/srv/rom/references/abu-msm-4.14-ksu/` | **abu reference kernel** (A15-era). `drivers/kernelsu` → symlink `KernelSU-Next/kernel` |
| `/srv/rom/references/resukisu/` | ReSukiSU git reference (non-shallow; version from `rev-list`) |
| `/srv/rom/references/susfs4ksu/` | Upstream SusFS project reference |
| `/srv/rom/artifacts/007-kernelsu/abu-reference/` | Owner-supplied abu `boot-abu` / `dtbo-abu` (reference only; **do not flash as ours**) |
### Laptop workspace
| Path | Role |
|------|------|
| `/home/aventura/Workspaces/sunfish-a16-rom/` | Git project |
| `.local-artifacts/007-kernelsu/` | Copied boots, klog dumps, Manager APKs |
| `docs/007-kernelsu-runbook.md` | Runbook |
| `docs/007-abu-vs-ours-susfs.md` | abu vs ours checklist |
| `docs/007-debug-boot-panic-screen.md` | Debug boot / photo Oops notes |
| `specs/007-kernelsu-sunfish/` | Feature spec/tasks |
**SSH:** `ssh a16-build` → `android-build@rom-dev01` (see `docs/ssh/config`).
---
## 3. Boot image changelog (chronological)
All under `/srv/rom/artifacts/007-kernelsu/` (and laptop `.local-artifacts/007-kernelsu/` when copied).
| Stamp / file | uname / notes | Config essence | LAB result |
|--------------|---------------|----------------|------------|
| **Penúltimo** `boot-resukisu-our-20260915T183729Z.img` | `#1 … Tue Sep 15 18:06:17 UTC 2026` | `MANUAL_HOOK=y` + all **AUTO_***=y; **SusFS off** | **GOOD** — daily LAB root |
| `boot-005-known-good.img` | Stock bacon kernel (no KSU in practice for root) | LOS boot from zip | Emergency recovery when slots broken |
| **SusFS #1** `…-susfs-20260916T004038Z.img` | `#3 … Wed Sep 16 00:43:54` | `CONFIG_KSU_SUSFS=y`, **`MANUAL_HOOK` unset** (choice XOR) | Kernel up, **adb alive**, bootanim forever: `system_server` stuck in **VibratorManagerService.nativeGetVibratorIds** (~66s watchdog) |
| **SusFS #2** `…-susfs-20260916T091211Z.img` | `#4 … Wed Sep 16 09:21:41` | After Kconfig patch: `MANUAL_HOOK=y` + SusFS; **AUTO_* all unset** | **Google logo → reboot loop** (earlier than #1) |
| **SusFS #3** `…-susfs-20260916T104649Z.img` | `#6 … Wed Sep 16 10:51:09` | `MANUAL` + **AUTO_***=y + SusFS; `susfs_init` in MANUAL path; link fix for `ksu_init_rc_hook` | Still logo→reboot. Proven on **slot a** (same system as penúltimo) → **not A/B system_b** |
| **SusFS #4 sigfix** `…-susfs-20260916T112839Z.img` | `#7 … Wed Sep 16 11:32:09` sha `472a32d8…` | + sucompat **faccessat/stat** signatures: SusFS `struct filename**` only if `SUSFS && !MANUAL` | **Worse** — returns to fastboot **faster** |
| **SusFS #5 DEBUG** (building at doc time) | `susfs-boot-debug` session | #4 macros fix (`susfs_*` privilege macros only if `!MANUAL`) + `PANIC_TIMEOUT=0`, no `QCOM_FORCE_WDOG_BITE_ON_PANIC`, `VT`+`FRAMEBUFFER_CONSOLE`, cmdline `panic=0 console=tty0`, `SPOOF_CMDLINE` off | Pending |
**Known-good rollback always:**
`boot-resukisu-our-20260915T183729Z.img` on `boot_a` + `--set-active=a`.
**Manager matching 35153:**
`ReSukiSU_v4.2.0-rc2-spoofed_35153-arm64-v8a-release.apk`
(without Manager, `su` missing even if kernel has KSU).
---
## 4. Root-cause chain (what we learned)
### 4.1 ReSukiSU upstream treats SusFS as a **hooking method** (XOR)
Kconfig `choice "KernelSU Hooking Method"` originally included `KSU_SUSFS` alongside `KSU_MANUAL_HOOK` / `KSU_TRACEPOINT_HOOK`.
Enabling SusFS in defconfig → warning `override: KSU_SUSFS changes choice state` → **MANUAL_HOOK off**.
**ikconfig proof (T004038Z vs penúltimo):** see
`/srv/rom/artifacts/007-kernelsu/susfs-fail-rca/ikconfig.diff`.
**abu / KernelSU-Next model (correct for addon):** SusFS is a separate `bool` menu under KSU, **not** in the hook choice. Hooks = KPROBES (or manual). `susfs_init()` under `#ifdef CONFIG_KSU_SUSFS` always.
### 4.2 First LAB symptom (T004038Z) — vibrator watchdog
- Not an early panic: Android mostly up, adb works.
- `WATCHDOG KILLING SYSTEM PROCESS` blocked in `VibratorManagerService.nativeGetVibratorIds`.
- Interpreted as fallout of **missing MANUAL_HOOK** (broken input/LSM hook path / module vermagic issues discussed; drv2624 is vendor module).
### 4.3 After forcing MANUAL+SusFS — early `kernel_panic`
Builds with MANUAL (+ later AUTO) + SusFS die at **Google logo**, reboot, often land in **fastboot** after retries.
**Logging reality on Pixel 4a:**
- Bootloader **does not support** `fastboot fetch`.
- `/dev/block/by-name/klog` and `logfs` dumps show many `PM: Reason: kernel_panic` / `HARD RESET by PS_HOLD` but **no Oops / Call trace**.
- `/sys/fs/pstore` empty after successful penúltimo boot; Pixel ramoops often encrypted (`/vendor/bin/ramoops`, keys under CE — missing/unusable in our tries).
- Console default: `console=ttyMSM0` only; `# CONFIG_VT is not set` historically → **no Oops text on panel** (only logo).
### 4.4 Composition bugs fixed in-tree (still not enough for a clean boot)
Patches applied under `drivers/kernelsu/` (and defconfig / BoardConfig):
| # | File(s) | Change |
|---|---------|--------|
| 1 | `Kconfig` + `Kbuild` | SusFS **out of** hook `choice`; SusFS addon + MANUAL together (abu-like). Snapshot: `susfs-fail-rca/kconfig-fix-20260916T090322Z/` |
| 2 | `arch/.../sunfish_defconfig` | Re-enable `AUTO_SETUID/INITRC/INPUT=y` (T091211Z had them explicitly **unset**) |
| 3 | `runtime/ksud_integration.c` | `#elif CONFIG_KSU_SUSFS` → only if `&& !CONFIG_KSU_MANUAL_HOOK` so `ksu_init_rc_hook` is defined under MANUAL+AUTO (else **link error** `undefined symbol: ksu_init_rc_hook`) |
| 4 | `core/init.c` | In `MANUAL_HOOK` branch, also `#ifdef CONFIG_KSU_SUSFS susfs_init()` (abu always inits SusFS) |
| 5 | `feature/sucompat.h` + `.c` | `faccessat`/`stat`: SusFS `struct filename **` API **only** if `SUSFS && !MANUAL`. MANUAL call sites in `fs/open.c` / `fs/stat.c` pass `const char __user **` — with SusFS signatures → **ABI mismatch / panic** |
| 6 | `feature/sucompat.h` | Privilege macros: SusFS `susfs_is_current_proc_no_su` etc. **only** if `SUSFS && !MANUAL` (was still SusFS when both set — likely early panic) |
| 7 | defconfig + BoardConfig (DEBUG) | `PANIC_TIMEOUT=0`, disable `QCOM_FORCE_WDOG_BITE_ON_PANIC`, enable `VT`/`FRAMEBUFFER_CONSOLE`, cmdline `panic=0 console=tty0` when `SUNFISH_DEBUG_PANIC_SCREEN=1`; `SPOOF_CMDLINE` off |
** empirically:** fix #5 (sigfix T112839Z) made LAB failure **faster** to fastboot → either wrong direction for some other call path, or #6 was the dominant bug and #5 alone left broken macros. #6 is in the DEBUG build, **not yet LAB-proven**.
### 4.5 Slot / A/B lesson (not the SusFS root cause)
- After failed trials, `current-slot:b`, `slot-unbootable:a:yes` left LAB unbootable until **both** slots got `boot-005` + `--set-active=a`.
- SusFS on **slot a** (same `system_a` as working penúltimo) **still** bootlooped → failure is **SusFS/MANUAL kernel composition**, not stale `system_b`.
**Flash discipline now:** penúltimo on `boot_a`, experiment on `boot_b`, `--set-active`; rollback = `--set-active=a` (and reflash `a` if needed).
---
## 5. Critical comparison: why T004038Z got further than MANUAL+SusFS
| | T004038Z (SusFS as hook choice) | T091211Z+ (MANUAL+SusFS addon attempts) |
|--|----------------------------------|----------------------------------------|
| MANUAL_HOOK | **off** | **on** (+ AUTO on later builds) |
| Early boot | Survives to userspace / adb | `kernel_panic`, logo loop |
| Later failure | Vibrator HAL / system_server watchdog | Never gets there |
**Hypothesis for reviewers:** ReSukiSU still has multiple `#ifdef CONFIG_KSU_SUSFS` paths written for **XOR** (SusFS *instead of* MANUAL). Turning both on without auditing every `elif` leaves:
- Wrong sucompat prototypes vs `fs/*.c` hooks
- Wrong privilege helpers (`susfs_*` vs TIF flags)
- Possibly more (execveat_sucompat, dispatch mark, input hooks, etc.)
abu avoids this by using **Next + KPROBES** and SusFS as a pure addon with `#ifdef CONFIG_KSU_SUSFS` init, not ReSukiSU’s choice XOR.
---
## 6. abu reference details (oracle, not flash path)
**Tree:** `/srv/rom/references/abu-msm-4.14-ksu/`
**KSU:** `drivers/kernelsu` → `../KernelSU-Next/kernel`
**Kconfig pattern (excerpt behaviour):**
- `CONFIG_KSU` tristate
- `CONFIG_KSU_KPROBES_HOOK` (default y) — not ReSukiSU MANUAL choice
- `menu "KernelSU - SUSFS"` → `CONFIG_KSU_SUSFS` bool addon + many `KSU_SUSFS_*` features
- `ksu.c`: `#ifdef CONFIG_KSU_SUSFS` → `susfs_init();` **unconditional on SusFS**, independent of kprobes
**Feature flags abu has that our ReSukiSU SusFS Kconfig may lack:**
`HAS_MAGIC_MOUNT`, `AUTO_ADD_SUS_*`, `TRY_UMOUNT*`, `SUS_SU` (kprobes), etc.
**abu `sunfish_defconfig` in this mirror:** **0** `CONFIG_KSU*` lines — do not treat as the SusFS pin list.
**Owner note:** abu also ships separate ReSukiSU-oriented boots / managers under artifacts; still **reference**. Our flash path is only `/srv/rom/artifacts/007-kernelsu/boot-resukisu-*` built from **our** tree.
---
## 7. Parallel work same day (context, not SusFS)
- microG Location UI missing on LAB: we had staged official **`*-hw.apk`** (`versionName=…-hw`) which **omits** `NetworkLocationService` / `NetworkLocationProviderService`.
- GOLDEN/abu show full Location options with **default** (non-hw) GmsCore.
- Fixed: stage `com.google.android.gms-252432032.apk` (non-hw); LAB `adb install -r` restored NLP UI.
- GOLDEN/abu = **A15 scheme oracle**; we keep **newer** packages on A16.
---
## 8. How we build / logs
```bash
# normal SusFS bootimage (host)
/srv/rom/artifacts/bin/rom-detach susfs-boot-NAME -- \
bash -lc /srv/rom/artifacts/007-kernelsu/run-susfs-bootimage.sh
# DEBUG (panic freeze + tty0)
/srv/rom/artifacts/bin/rom-detach susfs-boot-debug -- \
bash -lc /srv/rom/artifacts/007-kernelsu/run-susfs-bootimage-debug.sh
```
Always record:
- `/srv/rom/artifacts/detach-<session>-<TS>.log`
- `/srv/rom/logs/007-build/susfs-bootimage.log` (or `-debug.log`)
- `detach-<session>.status` (`exit=0` / `exit=1`)
Verify image before flash:
```bash
unpack_bootimg + lz4 -dc kernel + scripts/extract-ikconfig
# expect MANUAL_HOOK=y, three AUTO_*=y, KSU_SUSFS=y
```
---
## 9. LAB recovery / log capture protocol (4a)
```text
1. SusFS on boot_b, penúltimo on boot_a, --set-active=b, reboot
2. On logo loop: Vol- + Power → fastboot
3. fastboot fetch klog → FAILS on this device (unsupported)
4. fastboot flash boot_a <penúltimo>; --set-active=a; reboot
5. IMMEDIATELY on adb:
su -c 'dd if=/dev/block/by-name/klog …; dd if=/dev/block/by-name/logfs …'
adb pull → lab-klog-*.bin / lab-logfs-*.bin
6. strings | grep kernel_panic|Oops|Call trace|susfs
```
Observed: step 6 usually only `PM: Reason: kernel_panic` — **insufficient for stack**. Hence DEBUG on-screen build.
---
## 10. Current tree intent (defconfig snapshot at doc time)
```
CONFIG_KSU=y
CONFIG_KSU_MANUAL_HOOK=y
CONFIG_KSU_MANUAL_HOOK_AUTO_SETUID_HOOK=y
CONFIG_KSU_MANUAL_HOOK_AUTO_INITRC_HOOK=y
CONFIG_KSU_MANUAL_HOOK_AUTO_INPUT_HOOK=y
CONFIG_KSU_SUSFS=y
CONFIG_KSU_SUSFS_SUS_PATH=y
CONFIG_KSU_SUSFS_SUS_MOUNT=y
CONFIG_KSU_SUSFS_SUS_KSTAT=y
# CONFIG_KSU_SUSFS_SPOOF_UNAME is not set
CONFIG_KSU_SUSFS_ENABLE_LOG=y
CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS=y
# CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG is not set
CONFIG_KSU_SUSFS_OPEN_REDIRECT=y
CONFIG_KSU_SUSFS_SUS_MAP=y
# DEBUG bring-up:
CONFIG_PANIC_TIMEOUT=0
# CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC is not set
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
…
```
---
## 11. Open questions for a second opinion
1. Is ReSukiSU **fundamentally** unsafe to run with `CONFIG_KSU_MANUAL_HOOK=y` **and** `CONFIG_KSU_SUSFS=y` without a larger port from KernelSU-Next’s addon model (more than Kconfig)?
2. Given T004038Z reached userspace with SusFS-as-choice (no MANUAL), should bring-up prefer that path and fix vibrator separately, vs insisting on MANUAL+AUTO+SusFS?
3. Remaining `#ifdef CONFIG_KSU_SUSFS` sites to audit in ReSukiSU when MANUAL is also on (`sucompat.c` execveat_sucompat, `dispatch.c`, input, etc.)?
4. Best way to get a **panic backtrace** on Pixel 4a 4.14 without UART cable (fbcon reliability? pstore decrypt? `CONFIG_PSTORE_CONSOLE`?)?
5. Any known ReSukiSU + susfs4ksu commit/tag combo that officially supports “SusFS addon + manual hooks” on non-GKI 4.14?
---
## 12. Related docs in-repo
- `docs/007-abu-vs-ours-susfs.md`
- `docs/007-debug-boot-panic-screen.md`
- `docs/007-kernelsu-runbook.md`
- `docs/session-20260916-handoff.md`
- Host: `/srv/rom/artifacts/007-kernelsu/susfs-fail-rca/SUMMARY.txt` and `NOTE-*.txt`
---
## 13. Non-goals / constraints
- No Magisk as production root.
- Do not flash abu/torukk/Downloads boots as product.
- LAB only; never GOLDEN.
- Prefer small attributable patches; large Android builds on `rom-dev01` only, detached (`rom-detach`).
---
*End of brief — 2026-09-16 afternoon LAB session after T112839Z failure; DEBUG build in flight.*
+139
View File
@@ -0,0 +1,139 @@
# SUSFS bring-up: source and artifact audit, 2026-09-16
Status: **superseded for boot outcome** by
[007-lab-results-20260916.md](007-lab-results-20260916.md) — matched
DEBUG boot + vendor (correct SELinux) **booted**; SusFS v1.5.5 + Manager
35153 verified. This audit retains the ABI / stub / UAF analysis. Banking
PASS still FAIL. LAB only; GOLDEN was not accessed.
## Verified baseline and build
LAB reports `23.2-20260915-UNOFFICIAL-sunfish`, slot `_a`, kernel
`4.14.357-openela-g28f9290ae067-dirty #1 ... Sep 15 18:06:17 UTC 2026`.
Approved shell root works. The installed vibrator module probes successfully
on this kernel. The DEBUG build started at `2026-09-16T11:51:15Z` and was
still linking with `ld.lld` using a full CPU at 12:18Z. Ninja's idle-output
warning alone is not a stalled-build diagnosis. Its inputs were not edited
during this audit.
## Confirmed incompatible vendor module
The installed `/vendor/lib/modules/drv2624.ko` imports `module_layout` with
CRC `0xbce8f094`. The SUSFS sigfix build's `Module.symvers` exports it as
`0xf9adc508`. There are **31 mismatches among 56 imports**. The matching
module in the host output passes all 56 checks against that same symvers.
Evidence was captured before DEBUG replaced these outputs:
- Host symvers mtime: `2026-09-16 11:44:39 UTC`.
- Host vendor module mtime: `2026-09-16 11:45:48 UTC`.
- Kernel config: `CONFIG_MODVERSIONS=y`, module force loading disabled.
- Device module loading list: `device/google/sunfish/init.insmod.sunfish.cfg`
includes `drv2624.ko` alongside audio, touch and WLAN modules.
- Local evidence: `.local-artifacts/007-kernelsu/rca-20260916/`, including
`Module.symvers-sigfix`, `drv2624-installed.ko`, `drv2624-built.ko` and CRCs.
Reproduce with `python3 scripts/check-module-abi.py <Module.symvers> <module.ko>`.
Exit 1 means incompatible or unverified; exit 0 verifies CRCs only, not full
ABI/CFI/firmware compatibility or device behaviour.
**Implication:** flashing a SUSFS boot while retaining these installed vendor
modules is an invalid release combination. Rebuilding modules on the host
does not update the phone's vendor partition. The older vibrator watchdog
is consistent with this defect, but historical dmesg or a controlled test is
still needed to prove that particular attempt's cause. This result does not
prove the cause of the subsequent early panics.
SUSFS currently changes `task_struct` and `vfsmount`; those changes can affect
layouts and transitive exported symbol CRCs. Do not bypass version checks.
Package a consistent kernel/modules set and verify all imports before testing.
After vendor changes, the old boot alone is not necessarily a valid rollback;
preserve the corresponding old vendor/ROM and slot state too.
## Confirmed filename lifetime defect
Our `fs/exec.c:do_execveat_common()` calls `__do_execve_file()`, which calls
`putname(filename)` on both successful and failed exec paths. The wrapper
then calls `ksu_handle_post_execveat()`; ReSukiSU dereferences
`filename->name`. This is a use-after-free in the integration. It also exists
in the root-only path, so its existence does not establish why only SUSFS
panics in the recorded trials.
Prepared patch:
`vendor/sunfish/patches/007-kernelsu/0001-exec-avoid-post-hook-filename-uaf.patch`.
It calls `ksu_handle_post_execve()` with NULL filename, which that handler
explicitly supports for its LSM adapter. The patch is staged locally, not
applied to the running build, and still needs compilation and LAB validation.
## Confirmed incomplete and mismatched SUSFS API
The active kernel advertises SUSFS **v1.5.5**, based on upstream
`kernel-4.14` at `77905b5a071e6f3669e3b1814cea30147c0801da` (February 2025),
plus local compatibility wrappers. It is not a complete modern backport.
The active `fs/susfs.c` contains:
- `susfs_add_sus_map()`: reports success without implementing map hiding.
- `susfs_set_avc_log_spoofing()`: reports success without the SELinux hook.
- `susfs_add_sus_path_loop()`: aliases a one-time addition, with no loop list.
- `susfs_run_extra_works()` and `susfs_start_sdcard_monitor_fn()`: no-op.
- `susfs_get_enabled_features()`: returns only the generic KSU_SUSFS string.
The local `st_susfs_sus_path` starts with `err`, then `target_ino`, then a
pathname. Modern upstream v2.3.0 starts with the pathname, then `err`.
Sharing command numbers or converting a pointer type does not make those
wire formats compatible. Do not interpret compile/link success or a manager
feature badge as proof that the commands work.
[ReSukiSU integration documentation](https://resukisu.org/guide/build.html)
explicitly directs non-GKI SUSFS integrators to backport the 5.10 patches.
Its separate SUSFS hook mode is deliberate; the Kconfig choice is not in
itself an upstream bug. Missing manual hooks when selecting SUSFS means
the SUSFS integration must provide its own complete hook path.
## Upstream freshness checked without changing live inputs
Remote refs were queried directly on 2026-09-16:
- Latest ReSukiSU tag seen: `v4.2.0-rc2`,
`3576e6a5255fc880c00f6a3175d89268a6d942be`.
- Active ReSukiSU: `6d674e50a022a85076dcfe4498af9a8bfead2cf9`, nine commits
after that tag, non-shallow, rev-list count 4453, computed version 35153,
with local patches. Manager binary matching still requires independent
verification; the filename is not sufficient.
- Latest observed SUSFS 5.10 branch:
`e466df60670eab2847ea76940be2f8a125c8adc9`, advertising **v2.3.0**.
- Remote 4.14 branch still points to the old v1.5.5 commit above.
- Abu reference checkout `ksu-susfs-23.0` is
`8c604af9d3d97a8b1b2da1b663a4e39b32697e5a`, with SUSFS **v1.5.12**.
This checkout is not proof of the exact source/configuration of the
owner's working L22 binary. Do not conflate the two.
Before a new build, fetch tags, record full revisions and version codes, and
use a matching manager and userspace tool. A v2.3.0 port must include actual
implementations, correct struct layouts and all call-site adaptations.
## Panic evidence remains missing
The known-good dmesg shows the ramoops script exits 0, but inspection shows
that the script returns the final key-generation command's status, not the
earlier decrypt status. LAB has `vendor.ramoops.decryption.error=1`,
`use_alt=0`, and empty pstore. No private key contents were read or copied.
Therefore neither successful script exit nor an empty directory proves that
the failed kernel produced no backtrace.
Framebuffer console config does not guarantee a visible panic: display
registration and panic timing must also permit output. `panic=0` does not
by itself disable every independent hardware watchdog.
## Next diagnostic work
1. Preserve DEBUG as a named experiment with hashes/config/source snapshot.
2. Fix the lifetime defect in a subsequent isolated change.
3. Obtain panic trace or instrument milestones; distinguish kernel init,
module load, init and zygote failures rather than infer timing from logo.
4. Use a matched kernel/vendor-module deployment and rollback. Never compare
different system slots as though only boot changed.
5. Complete a modern SUSFS backport with the intended hook model, then verify
real command responses, root denial, per-UID hiding and hardware behaviour.
Boot success alone does not meet the requested result or feature gates.
+115
View File
@@ -0,0 +1,115 @@
# Design: native call-recording unlock + FLAG_SECURE override (`008`/`009`)
**Date**: 2026-09-16
**Status**: Approved by owner (requirements); Spec Kit feature
`specs/008-call-rec-flag-secure/`
**LAB only**: `11231JEC207477`. No Magisk/LSPosed/Zygisk for these features.
## Problem
1. Lineage Dialer can refuse call recording based on **country / carrier**
rules even when the user wants to record.
2. Apps set `FLAG_SECURE` so screenshots / screen capture fail; we want an
optional **global** override (project-specific; not AXP Pro catalog).
## Goals (MVP)
Two **Developer Options** switches (same UX pattern as `005`
`allow_signature_fake`):
| Toggle (plain language) | Secure setting | Default | Effect when ON |
|-------------------------|----------------|---------|----------------|
| Disable call recording restrictions | `extendrom_call_recording` (GOLDEN / EXTENDROM) | `0` | Country/region limits ignored; Dialer may record |
| Allow screenshots of secure windows | `disable_secure_windows` (AOSP; DO pref `allow_flag_secure_override`) | `0` | Global: ignore `FLAG_SECURE` for screenshots / screen capture (debuggable builds) |
- **Global only** — product requirement; no per-app lists (not planned).
- OFF = stock Android / LOS behaviour.
- No root modules required at runtime.
## Non-goals
- Full AXP call-recording policy UI (auto-record rules, etc.).
- Per-app FLAG_SECURE lists (out of scope permanently for this project).
- Banking / KernelSU (`007` incomplete).
- Shipping Magisk zips.
## Approaches considered
### Call recording
| Approach | Pros | Cons |
|----------|------|------|
| **A. EXTENDROM / AXP GOLDEN native F_CALLREC** (chosen) | Same Secure key as GOLDEN (`extendrom_call_recording=1` observed); DO + Dialer gate; attributable | Re-validate paths on LOS 23.2 / A16 |
| B. Magisk Dialer RRO (`call_record_states.xml`) | Tiny | Not native; no GOLDEN parity; rejected |
| C. Blind-copy A11 overlays only | Fast | May no-op without Dialer Java gate |
**Choice: A.** Primary sources:
`/srv/rom/references/extendrom/er.sh` (F_CALLREC),
`overlays/call-rec`, `config/call-rec/…/CallRecPreferenceController.java`.
Secure string: `Settings.Secure.ER_ALLOW_ANY_CALL_REC = "extendrom_call_recording"`.
Also enable Dialer `call_recording_enabled` / audio source as needed for
sunfish (EXTENDROM sets `call_recording_enabled=true`,
`call_recording_audio_source=4`), after verifying LOS 23.2 Dialer still uses
those resources.
### FLAG_SECURE
| Approach | Pros | Cons |
|----------|------|------|
| **A. DO → AOSP `DISABLE_SECURE_WINDOWS`** (chosen) | Already in WMS on A16; tiny surface | Debuggable-only gate in stock WMS |
| B. Magisk `services.jar` patcher | Works on `user` | Not native ROM |
| C. Duplicate frameworks hooks under another Secure key | — | Redundant with AOSP |
**Choice: A.** Wire Settings DO to `disable_secure_windows`.
## Design
### Settings / UI
- Category under Developer Options (new or shared “Privacy overrides” /
EXTENDROM-style categories).
- Two `SwitchPreference`s + short legal/risk summaries (call recording:
user responsible for local law — EXTENDROM wording as reference).
- Controllers mirror `005` / EXTENDROM `DeveloperOptionsPreferenceController`:
write `Settings.Secure` 0/1; reset to 0 when Developer Options master is
disabled.
### Call recording path
1. Inventory LOS 23.2 Dialer: where country/carrier recording is blocked
(`isCountryAllowed` or successor).
2. When `extendrom_call_recording=1`, treat as allowed (GOLDEN).
3. Ensure recording feature is compiled/enabled for Dialer on sunfish
(`config.xml` / product overlay as required).
4. LAB: enable toggle → place/receive call → record → file exists; disable →
region block behaviour restored (or stock deny if in restricted region).
### FLAG_SECURE path
1. Confirmed A16: `WindowManagerService` + `WindowState.isSecureLocked()` already
honour `Settings.Secure.DISABLE_SECURE_WINDOWS`.
2. DO switch writes that key (UI title: Allow screenshots of secure windows).
3. LAB userdebug: OFF blocked / ON capturable; document DRM exceptions.
### Packaging
- Patches under `vendor/sunfish-a16/patches/008-…` (idempotent apply script
like `005-sigspoof`), attributed to EXTENDROM/`er.sh` F_CALLREC (call-rec)
and native frameworks work (FLAG_SECURE).
- No Magisk in the bacon zip; no Magisk zips in the meta-repo.
### Validation
- LAB serial only; document settings get/put and screenshots/logs under
`/srv/rom/artifacts/008-…`.
- Does not reopen `007` banking gates.
## Relationship to roadmap numbers
| Roadmap # | Theme | This feature |
|-----------|--------|--------------|
| 008 | Call recording | Included |
| 009 | FLAG_SECURE | Included (global only) |
Single Spec Kit directory: `specs/008-call-rec-flag-secure/`.
+68
View File
@@ -0,0 +1,68 @@
# Runbook: `008` call-rec + FLAG_SECURE (LAB)
**LAB serial**: `11231JEC207477` only — never GOLDEN.
**Apply** (on `rom-dev01`):
```bash
python3 /srv/rom/src/lineage/vendor/sunfish-a16/patches/008-call-rec-flag-secure/apply_008_call_rec_flag_secure.py
```
Idempotent (second run = all “already”). Marker:
`SUNFISH_A16_008_CALL_REC_FLAG_SECURE`.
**Evidence**: `/srv/rom/artifacts/008-call-rec-flag-secure/`
**Logs**: `/srv/rom/logs/008-build/`
**Backups**: `/srv/rom/artifacts/008-call-rec-flag-secure/bak-snapshots/`
## Secure keys
| Key | Default | Source |
|-----|---------|--------|
| `extendrom_call_recording` | `0` | AXP GOLDEN / EXTENDROM (`ER_ALLOW_ANY_CALL_REC`) |
| `disable_secure_windows` | `0` | AOSP A16 `Settings.Secure.DISABLE_SECURE_WINDOWS` (DO UI key `allow_flag_secure_override`) |
GOLDEN dump: `extendrom_call_recording=1` in
`.local-artifacts/002-golden/…/settings/secure-*.txt`.
## Call-rec — EXTENDROM `F_CALLREC` (applied)
| Step | LOS 23.2 path | Status |
|------|---------------|--------|
| Dialer config | `packages/apps/Dialer/.../callrecord/res/values/config.xml` | already `enabled=true`, audio `4` |
| Controllers | `Settings/.../ER_CallRecPreferenceController.java` + `ER_CallRecInfo.java` | applied |
| Dashboard | `DevelopmentSettingsDashboardFragment.java` | applied |
| DO XML | `Settings/res/xml/development_settings.xml` | applied |
| Strings | `Settings/res/values/strings.xml` | applied |
| Secure const | `frameworks/base/.../Settings.java` `ER_ALLOW_ANY_CALL_REC` | applied |
| Dialer gate | `Dialer/.../CallRecorder.java` `canRecordInCurrentCountry` | applied |
## FLAG_SECURE — A16 inventory (applied via DO only)
AOSP already implements the capture path:
- `WindowState.isSecureLocked()` → early false if
`mWmService.getDisableSecureWindows()`
- `WindowManagerService.updateDisableSecureWindows()` reads
`Settings.Secure.DISABLE_SECURE_WINDOWS`
- **Only when** `ro.debuggable` / `SYSTEM_DEBUGGABLE` is true (LAB
`userdebug` OK; `user` needs retest / possible gate lift later)
Project work: Settings DO writes that Secure key (no Magisk / no jar patcher).
## Validation
See `specs/008-call-rec-flag-secure/quickstart.md`.
After bacon + flash Magisk-free:
```bash
adb -s 11231JEC207477 shell settings get secure extendrom_call_recording
adb -s 11231JEC207477 shell settings get secure disable_secure_windows
```
## `user` vs `userdebug`
LAB = `userdebug` (DO + WMS secure-windows disable active). Before release
(`011`), re-check on `user`: DO visibility (like `005`) and whether
`updateDisableSecureWindows` still no-ops when not debuggable — document
outcome; do not silently claim `user` PASS.
+55
View File
@@ -0,0 +1,55 @@
# Documentation
Project rules: `.specify/memory/constitution.md`
Agent rules: `../AGENTS.md`
Feature specs: `../specs/`
Conversation continuity: [`chatgpt-context.md`](chatgpt-context.md) — context
from the shared ChatGPT discussion, reconciled with later repository records;
non-normative.
## Host boundary
`rom-dev01` (Ubuntu 24.04, public IPv4, SSH) is provisioned **outside** this
project (SSYSTEMS Salt `saltenv=base` / PVE). This repository must not specify
or automate Proxmox VM creation, public IP selection, or company Salt states.
Feature `001-development-environment` starts from an already-available host.
## Planned Gitea repositories (on rom-dev01)
- `sunfish-a16-rom` — Spec Kit / constitution / docs (meta)
- `sunfish-rom-manifest`
- `sunfish-rom-build`
- `sunfish-rom-patches`
Plus forks only when modification requires them (kernel, frameworks/base,
Settings, Dialer, vendor/config).
## Reference sources (Lineage / AXP / GrapheneOS)
[`reference-sources.md`](reference-sources.md) — roles, URLs, local vs later
`rom-dev01` archive work. **Sunfish only.**
## Device roles (two sunfish)
[`device-roles.md`](device-roles.md) — GOLDEN (AXP, do not flash) vs LAB (experiments).
## AXP sunfish preservation (detail)
[`axp-sunfish-preservation.md`](axp-sunfish-preservation.md) — Disroot/Codeberg/
downloads/Tor cookbook for sunfish-scoped AXP archive. Run after `001` on
`rom-dev01`. **Not** a full AXP org clone.
## AXP Pro parity
Living matrix (non-normative): [`axp-pro-parity.md`](axp-pro-parity.md).
## Android 15 → 16 developer delta
Living notes for porting AXP/GrapheneOS ideas onto LOS 23.2 without blind copy:
[`a15-a16-delta.md`](a15-a16-delta.md).
## Hardening backlog
Non-normative candidates: [`hardening-backlog.md`](hardening-backlog.md).
+129
View File
@@ -0,0 +1,129 @@
# Android 15 (AXP GOLDEN) → Android 16 (LOS 23.2) — developer delta
Living notes for porting **AXP / GrapheneOS ideas** onto our base without
blind copy. Non-normative until a row is promoted into a feature `spec.md`.
| Ours (LAB target) | GOLDEN (oracle) |
|-------------------|-----------------|
| LineageOS **23.2** / Android **16** | AXP Pro / LOS-derived **22.2** / Android **15** |
| `BUILD_ID=BP4A.251205.006`, SDK **36** tree | SDK **35**, `ro.build.version.release=15` |
| Device: sunfish (same silicon) | sunfish |
**Working rule (owner):** prefer AXP A15 scheme when still valid; if A16 is
easier, use it; if harder, redesign the *path* to the **same end state**.
---
## 1. Already measured on this project
| Area | A15 / AXP behaviour | A16 / LOS 23.2 reality | Adaptation |
|------|---------------------|------------------------|------------|
| Phonesky on `/system/priv-app` | Works on GOLDEN | `artifact_path_requirements` / `generic_system.mk` rejects device-specific `system/` installs unless allow-listed | Keep AXP path; `PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST` (`004`) — do **not** move to `product` only to silence the check |
| GmsCore / Gsf on `system_ext` app | GOLDEN layout | Builds clean (no artifact hit) | Keep |
| Priv-app XML for GmsCore | GOLDEN: not priv-app | Inventing `privapp-permissions-microg` diverges | Removed; add only if Self-Check proves need |
| Super partition headroom | AXP fuller image fits their budgets | Vanilla LOS ERROR_LIMIT too tight for fat microG+Play | Modest `BOARD_SUPER_PARTITION_ERROR_LIMIT` bump — not ABI-strip of official microG |
| Play targetSdk / apktool | Older Play on GOLDEN (50.x) | Latest Play ~52.x needs apktool 2.12 + `ANDROIDSDK=35+`, zipalign-before-sign | Documented in `004` runbook |
| PRESIGNED prebuilt APKs | AXP ships Play/microG with intact v2/v3 | `BUILD_PREBUILT` uncompresses JNI / realigns → strips APK Sig v2/v3; PM skips Phonesky (`pm path` empty) | `LOCAL_REPLACE_PREBUILT_APK_INSTALLED` for GmsCore/Gsf/Phonesky (`004`, measured 2026-09-15) |
| Phonesky privapp allowlist | AXP XML for older Play | Play 52.x on A16 requests `HIDE_STATUS_BAR_NOTIFICATION` + `READ_SYSTEM_GRAMMATICAL_GENDER`; missing → boot abort on userdebug | Extend `privapp-permissions-com.android.vending.xml` |
| Signature spoofing | EXTENDROM: `allow_signature_fake` + `FAKE_PACKAGE_SIGNATURE` + Settings toggle; grants on gms+vending; A15 STABLE | LOS 23.2 **already** has LuK1337 allow-list spoof (`ComputerEngine` `23c0a5c`): userdebug + microG signer + Google fake-sig + `signingInfo`. Missing vs AXP: no `FAKE_PACKAGE` perm/UI; **rejects project-signed Phonesky** | Feature **`005`**: extend signer allow-list for our Phonesky JKS; port EXTENDROM toggle + `FAKE_PACKAGE_SIGNATURE`; see `docs/005-spoofing-runbook.md` |
---
## 2. Partition / packaging (porter mindset)
- AOSP has been tightening **system vs system_ext vs product** for years; A16
builds (especially with `generic_system.mk`) make “drop OEM APKs on system”
a **build-time** problem, not only a policy preference.
- **Privapp-permissions must live on the same partition as the priv-app**
(AOSP rule since product partition work). Moving Phonesky later ⇒ move XMLs
with it.
- `system_ext` remains the natural home for ROM Google-compat substitutes
(AXP: microG, F-Droid, Aurora base) — still valid on A16.
- Prefer **allow-list exception** or **system_ext/product** over rewriting the
whole AXP layout when GSI rules bite.
---
## 3. Compatibility stack (microG / Play / spoofing)
| Topic | Note for A16 |
|-------|----------------|
| microG | Use **current** upstream; do not pin GOLDEN 0.3.15. Keep official signer for F-Droid. |
| Phonesky | Still patch via AXP `patch-playstore`; newer Play = newer apktool/SDK framework. |
| Signature spoofing | **Done in `005`:** extend LuK1337 allow-list for project Phonesky JKS; port EXTENDROM toggle + `FAKE_PACKAGE_SIGNATURE`. LAB US6 green 2026-09-15. |
| Phonesky FGS / exact-alarm | A16 `systemExempted` needs manifest `USE_EXACT_ALARM` (or SCHEDULE / VPN), not only privapp XML. Fixed in Phonesky rebuild; vendor prebuilt updated. |
| FakeStore | Still forbidden next to real Phonesky. |
---
## 4. Attestation / KeyMint / banking (`006`/`007`)
- GOLDEN already shows **KeyMint / RKP / TEE** stress with Yuh on **A15**.
- A16 does not remove that class of problem; RKP and attestation surfaces
continue to evolve — treat GrapheneOS/AOSP docs as *what* to measure, LOS+LAB
as *where* to measure.
- Magisk DenyList on GOLDEN ≠ our root plan. Production root = **KSU-Next +
SusFS** (`007`). Do not port Magisk-shaped packaging into `004`.
---
## 5. GrapheneOS on Android 16 (reference, not base)
- GOS ported features to A16; publicly noted **harder** device-support work
because AOSP dropped much Pixel board/userspace publishing with 16.
- For **sunfish** we stay on **LOS 23.2 device trees**, not GOS device bringup.
- Use modern GOS as reference for **hardening / privacy behaviour** patches
(`013`, hardening backlog) — classify each patch: direct / port / obsolete /
A16-incompatible (same discipline as `reference-sources.md`).
- Never treat historical GOS **A13 sunfish** kernel as A16 base.
---
## 6. Call recording / FLAG_SECURE / UX (`008`+)
- AXP overlays and older XML toggles often **break or no-op** across major
releases. On A16: re-find Dialer/Telecom/framework hooks in LOS 23.2 first;
then decide AXP vs EXTENDROM vs iodé vs native LOS.
- Screenshot / sensitive-content APIs continue to tighten — verify before
porting FLAG_SECURE bypasses.
---
## 7. Checklist before applying an AXP or GOS change on A16
1. What problem does it solve on GOLDEN / in GOS docs?
2. Does LOS 23.2 already provide an equivalent?
3. Partition + artifact-path impact?
4. Same-signer / update path impact (microG, Phonesky)?
5. Kernel 4.14 / sunfish EOL constraints?
6. If blocked: document blocker; redesign route; **same end state**.
---
## 8. Open follow-ups (fill as we hit them)
- [x] Exact LOS 23.2 spoofing surface for `005` — stock LuK1337 path + our
`SUNFISH_A16_005_SIGSPOOF` extension (2026-09-15); runbook
`docs/005-spoofing-runbook.md`
- [ ] Whether A16 needs any microG permission XML AXP ships outside privapp
- [ ] RKP/KeyMint behaviour on LAB after `004` (compare to `002` Yuh logs)
- [ ] Call-recording hook locations on LOS 23.2 Dialer
- [ ] Which GOS A16 hardening patches are realistic on sunfish 4.14
## 9. EXTENDROM A15 → A16 mapping (`005`)
| EXTENDROM A15 patch | LOS 23.2 action |
|---------------------|-----------------|
| `02`/`06` ComputerEngine FAKE + gate | **Adapt** existing LuK1337 `generateFakeSignature`; add toggle + permission + Phonesky signer |
| `01`/`12` api current + lint | Applied |
| `07` AndroidManifest permission | Applied |
| `03` PermissionMapping | Applied (`.kt` path) |
| `04` Activity requestPermissions | Applied (A16 short-circuit path context) |
| `05` Settings.Secure.ALLOW_SIGNATURE_FAKE | Applied |
| `08`–`11` Settings Developer Options | Applied (controllers + XML + strings) |
| Overlay strings | Inlined into frameworks/Settings `strings.xml` |
Source checkout: `/srv/rom/references/extendrom` @ `f129253c07e735a373441254457cf75efac11d04`.
Update this file when a delta is **measured** (build error, Self-Check, adb),
not from blog speculation alone.
+95
View File
@@ -0,0 +1,95 @@
# AXP.OS Pro feature parity matrix
Living tracker for how `sunfish-a16-rom` relates to **AXP.OS Pro** functionality.
**Non-normative.** This file is a product/backlog tracker. A row becomes a
requirement only when promoted into a feature `spec.md`. For an active feature,
constitution + that feature's `spec.md` are authoritative if they conflict with
this matrix.
**Goal (constitution):** reproduce AXP.OS Pro functionality except where a
feature is obsolete, incompatible with LineageOS 23.2 / Android 16 / sunfish, or
deliberately substituted (documented in this table).
**Do not** treat this as a blind copy list. Every row must state our target and
implementation source. Update status as features land.
Status values: `todo` | `decide` | `substitute` | `wont` | `partial` | `done` |
`los-provides` | `later`
The parity matrix **spans the whole project (`004`–`013`)**. Feature `010` owns
only accepted AXP **usability/privacy** items not already assigned to another
feature. Signing/AVB, userspace hardening, and kernel hardening remain `011`–
`013` even though they also appear here as AXP Pro concerns.
| Feature | AXP Pro | Our objective | Status | Source / implementation |
|---------|---------|---------------|--------|-------------------------|
| Base ROM | AXP (LOS-derived) | LineageOS 23.2 / Android 16 sunfish | todo | LOS official `lineage-23.2`; `003` |
| On-device release testing | Yes (pre-release device QA) | Yes — stage checklists + known-good builds | todo | `003` smoke; later release gate; `011` |
| ASB patch integration / backports | Yes | Prefer LOS merges; backport missing applicable patches; document non-applicable | todo | LOS first; gaps → `012`/`013` |
| SELinux enforcing | Yes | Expect **Enforcing**; measure on vanilla then keep | todo | Verify in `003`; then likely `los-provides` |
| Data encryption enforced | Yes | Expect encryption enabled; no silent weaken | todo | `003` PIN/encrypt smoke; then likely `los-provides` |
| Project signing | Yes | Own keys for release builds | todo | `011` |
| Strong signing / AVB key policy | Yes (AVB/APK/dm-verity hardening) | Project AVB + release signing policy | todo | `011` |
| Reproducible builds | Yes | Traceable builds; push toward reproducibility | todo | metadata from day one; harden in `011` |
| Privacy-focused system defaults | Yes | Decide defaults beyond LOS stock | decide | `010` |
| microG + GSF | Yes | Yes — working Self-Check / push | done | `004` packaging + `005` LAB US6 (`23.2-20260915`) |
| Patched Phonesky / Play | Yes | Yes — priv-app, updatable | done | Play 52.9.22-24 + exact-alarm; installs OK on LAB (`005`) |
| Signature spoofing | Advanced / configured | Restricted LOS + Phonesky JKS + DO toggle | done | `005` runbook; `allow_signature_fake` + `FAKE_PACKAGE` |
| Magisk / preroot | Magisk (**Magia** UI on GOLDEN, 29.0) | **Substitute:** KernelSU-Next (+ SusFS) / evaluate ReSukiSU | substitute | Captured in `002`; do **not** fix GOLDEN; project root = `007` on LAB |
| DenyList / umount for banks | Magia DenyList (pkg list in `docs/002-golden-inventory.md`) | KernelSU app profiles + verified umount | todo | `007` re-runs `006`; GOLDEN DenyList captured `002` |
| Insular work profile | AXP docs say Insular; **GOLDEN measures Island** (`com.oasisfeng.island.fdroid` 6.4.2) | Banking workflow parity (Island or equivalent) | todo | `006`; capture in `002` |
| Yuh in Insular/Island | Historically worked; **FAIL since ~weeks** (HA / “certificate invalid”; KeyMint/TEE errors in `002` logs) | Hard-gate Compatibility MVP (rootless in `006`); **Revolut not a hard-gate** | partial | `002` evidence; LAB may need SusFS+TrickyStore-class stack (`reference-sources.md` §8); never GOLDEN |
| Swissquote in Insular/Island | **PASS** on GOLDEN (owner report, DenyList) | Hard-gate Compatibility MVP (rootless in `006`) | partial | `002`; re-test in `006`/`007`/`011` |
| Unrestricted call recording | Yes | Yes — native ROM on Android 16 | todo | AXP/EXTENDROM/iodé as references only; re-validate on LOS 23.2; `008` |
| DNS blocker / privacy DNS | Yes | Wanted; exact UX TBD | decide | `010` |
| Hosts / tracker blocking | Yes | Wanted | decide | `010` |
| hardened_malloc | Yes | Wanted if cleanly portable | decide | Graphene/AXP; `012`; see hardening backlog |
| Hardened / CVE-patched kernel | Yes | LOS 23.2 kernel + Graphene/AXP/Divest backports | todo | `013` (+ selective work with `007`) |
| Deblobbing | Yes | Case-by-case; don't break HW | decide | `010` / `013` |
| AOSmium WebView | Yes | Ship usable WebView; allow installing alternate WebView providers and selecting the active one in **Developer options → WebView implementation** | decide | `010`; stock/AOSmium/Vanadium/etc. as candidates |
| WireGuard in kernel | Yes | Check if LOS 23.2 already sufficient | decide | Baseline in `003`; else `010`/`013` |
| eSIM support | Documented on AXP | sunfish support TBD; verify vanilla first | decide | Baseline in `003`; else `010` |
| F-Droid + extra repos | Yes | Decide default ship set | decide | `010` |
| Aurora Store | Used in golden banking flow | Document/capture; may remain user app | decide | `002` flow; `010` |
| NeoLauncher | Default on AXP | Decide LOS launcher vs NeoLauncher | decide | `010` |
| Fossify Gallery / bundled app set | Part of AXP usability set | Decide which extras to ship | decide | `010` |
| Boot debug | Available | Useful for bring-up | decide | `010` or early debug docs |
| Relock / custom AVB | AXP path | Project keys + AVB after stable ROM | todo | `011` |
| Widevine (basic) | Yes | Don't regress vs vanilla LOS if possible | decide | Measure in `003`; track in `010`/`011` |
| App/data backup | AXP tooling | Not a project gate (two devices: GOLDEN stays; LAB is disposable). Optional personal NeoBackup only; no custom ROM/recovery backup | substitute | See `docs/device-roles.md` |
| Extra bundled packages | Various | Only if justified | decide | `010` |
| Auto-reboot / radio timers | Privacy extras | Wanted where practical | decide | `010` |
## Mapping rules
| AXP mechanism | Our default approach |
|---------------|----------------------|
| Magisk | KernelSU-Next (`007`); Magisk only as A/B diagnostic if needed |
| AXP signature spoofing stack | LOS restricted spoofing + controlled Phonesky extension (`005`) |
| AXP hardened kernel tree | LOS sunfish kernel + selective backports |
| AXP Phonesky | Keep functionality; reproducible patch/sign pipeline |
| AXP call-recording toggles (older Android) | Re-validate on Android 16; do not blind-port XML |
| AXP backup/restore tooling | NeoBackup (external); no project-owned backup feature |
| AXP AOSmium / extras | Explicit decide rows above — not silent omissions |
| WebView provider | Keep Android multi-provider path: install other WebViews + choose in Developer options |
## Project-specific additions
Not AXP Pro catalog features. Tracked here so they are not confused with parity.
| Feature | Origin | Objective | Status | Feature # |
|---------|--------|-----------|--------|-----------|
| FLAG_SECURE override | Project requirement | Native control: Android default remains default; **global** DO override only (no per-app); screenshots + screen capture; no LSPosed/Zygisk/root module | todo | `009` / `008-call-rec-flag-secure` |
| Optional root-unavailable boot mode | Project / msg-GrapheneOS inspiration | Diagnostic/fallback boot without root | later | after daily-driver MVP |
| Upstream deprecation / Kotlin warning hygiene | Build log noise on LOS 23.2 | **Last steps before final ROM freeze:** triage remaining compile warnings; migrate call sites API-by-API (or inherit LOS fixes); keep our patches warning-clean. Not a gate for `005`–`010` MVPs. | later | after `010` / with `011` freeze; see `docs/hardening-backlog.md` |
## Ownership
- Matrix maintained in this file (`docs/axp-pro-parity.md`).
- Rows may map to `004`–`013`; **`010` is not “all of AXP Pro parity”** — only
remaining usability/privacy items not owned elsewhere.
- `002` captures what the **current device** actually runs; this matrix is the
**product intent** relative to AXP Pro docs + that capture.
- Compatibility MVP ends at `006` (rootless); Daily-driver MVP ends at `010`
(constitution). Hardening candidates: `docs/hardening-backlog.md`.
+510
View File
@@ -0,0 +1,510 @@
# AXP.OS sunfish preservation / reference archive
Parent overview (Lineage + AXP + GrapheneOS): [`reference-sources.md`](reference-sources.md).
## Purpose
AXP.OS is an important historical and functional reference for
`sunfish-a16-rom`.
Some AXP.OS infrastructure is self-hosted and may disappear. Preserve **useful
publicly accessible material related to sunfish / AXP.OS A15** before relying on
it during later ROM development.
This archive is **REFERENCE** material. It is **not** the base ROM.
```text
GOLDEN device: AXP.OS 22.2 / Android 15 on sunfish
historical GOS: Android 13 sunfish (hardening reference only)
new ROM base: LineageOS 23.2 / Android 16
AXP sources: reference inputs only
```
**Do not** block ROM development indefinitely on recovering unavailable AXP
material.
**Do not** archive all ~225 Disroot repositories (Sony, OnePlus, Fairphone,
unrelated Pixels, etc.).
```text
AXP archive != complete backup of AXP.OS
AXP archive == what is needed to understand, reproduce, and port
AXP.OS A15 for SUNFISH
```
Execute this work on **`rom-dev01`** under `/srv/rom/references/axp/`. Do not
keep growing large mirrors on the laptop once the server copy is verified.
---
## When to run
- After `001` has `/srv/rom` and `android-build` SSH working.
- Before or in parallel with early `002` (does not replace GOLDEN live `adb`).
- Prefer rsync of the existing laptop archive first, then fill gaps on the server.
---
## Architecture reminder
| Role | Meaning |
|------|---------|
| GOLDEN | Live AXP.OS 22.2 / A15 — do not flash for experiments |
| LAB | LOS / project ROM experiments only |
| This archive | Offline docs + sunfish-related git/releases on `rom-dev01` |
See also: `docs/device-roles.md`.
---
## 1. Known AXP.OS hosting
### Main website
Preserve (rendered + git source):
- https://axpos.org/
- https://axpos.org/news/
- https://axpos.org/docs/
- https://axpos.org/devices/
- https://axpos.org/devices/google/sunfish/
- https://axpos.org/devices/changelogs/2026/
- https://axpos.org/docs/knowledge/faq/
- https://axpos.org/docs/knowledge/reproducible/
- https://axpos.org/docs/knowledge/microg/
- https://axpos.org/docs/knowledge/callrec/
- https://axpos.org/docs/knowledge/features/
- https://axpos.org/docs/knowledge/technical-details/
Website hosting: Codeberg.
Website source:
- https://codeberg.org/AXP-OS/axpos-org
- https://codeberg.org/AXP-OS/axpos-org.git
Observed branches: `hugo`, `pages`.
Official docs describe:
| Host | Role |
|------|------|
| **Disroot** (`git.disroot.org/AXP.OS`) | Main **public** mirror of repos / APK releases / reproducible-build data |
| **code.binbash.rocks/AXP.OS** | Complete self-hosted set (CI, internals) — **not** anonymously public |
| **Codeberg** | Partial (website + fewer repos after Jan 2026 removals) |
| **GitHub** (`github.com/AXP-OS`) | Mainly APK/release mirror |
---
### Public source mirror: Disroot
- Org: https://git.disroot.org/AXP.OS
- API: https://git.disroot.org/api/v1/orgs/AXP.OS/repos
Snapshot **2026-08-09** (inventory only — not a mandate to clone everything):
- Repos: **225**
- Forgejo reported size: **49,908,145 KiB (~48 GiB)** Git metadata only
- **Additional** space needed for LFS, releases, wikis, downloads
Generate inventory dynamically from the API. Do **not** maintain a hard-coded
full org clone list.
---
### Codeberg
- https://codeberg.org/AXP-OS
- Snapshot 2026-08-09: **14** public repos, complete attempt ~**38 GiB**
- Includes `axpos-org` website source
- **Not** a complete AXP source archive (100+ repos removed Jan 2026 per AXP docs)
Known laptop archive: `~/Archives/AXP.OS/codeberg/`
Known issue: `legacy_prebuilt_apps.git` has historical LFS pointers whose objects
return **HTTP 404** on Codeberg.
```text
"Git mirror valid" != "all historical LFS content preserved"
```
Do **not** delete the Codeberg archive just because Disroot is newer/larger.
---
### GitHub
- https://github.com/AXP-OS
- Documented focus: APK/release mirror (PhoneSky, AOSmium, OpenEUICC, …)
- Lower priority; useful independent source for artifacts/public keys
---
### Self-hosted Gitea (not publicly readable)
- https://code.binbash.rocks / https://code.binbash.rocks/AXP.OS
- Observed DNS 2026-08-09: `148.251.225.181`
- Site root HTTP 200; anonymous org API returned `[]`
- Historical repo URLs request auth; web 404
**Do not** attempt to bypass access controls. Only use legitimately public
material. Official docs: this host holds the complete private set.
---
## 2. Download / OTA infrastructure
### Clearnet downloads
- https://download.axpos.org (DNS 2026-08-09 → `148.251.225.182`, PTR yes.binbash.rocks)
- Root `/` may time out; **always probe exact paths**
Sunfish paths:
```text
PRO: https://download.axpos.org/axp/sunfish/
…/factory/ …/recovery/ …/tests/
SLIM: https://download.axpos.org/axp-slim/sunfish/
```
Device page confirms **AXP.OS 22.2 / Android 15** for Pro and Slim, with links
to OS zip, recovery, factory, and keys.
When reachable, preserve for sunfish (Pro + Slim as available):
- OS ZIPs, `.prop`, `.sha512sum`
- recovery / factory packages
- relevant test artifacts
- signing / AVB **public** keys
Never treat images as trusted without published checksums/signatures.
Example filenames seen in indexes (verify live):
```text
AXP.OS-22.2-20260324-PRO-sunfish.zip(+.prop+.sha512sum)
AXP.OS-22.2-20260331-PRO-sunfish.zip(+.prop+.sha512sum)
```
### Tor download mirror
Onion advertised by AXP:
```text
e3tx35xwvqoihx36tjmnzurjcghs6kjwrwnen55jg7ujqilbaicutpid.onion
HTTP :8008 (main documented alias)
HTTPS :9009 (historical)
```
Example:
```text
http://…onion:8008/axp/sunfish/
http://…onion:8008/axp-slim/sunfish/
```
Probe when preservation runs; do not assume availability.
### Legacy download host
`leech.binbash.rocks` — observed unavailable 2026-08-09 (timeouts / refused).
Treat as legacy; `download.axpos.org` replaced port-8008 URLs per changelog.
### OTA
```text
https://update.axpos.org
https://update-unstable.axpos.org
```
Both resolved to `148.251.225.182` (2026-08-09); `/` → HTTP 403 Apache.
403 on `/` ≠ OTA API dead (updater endpoints, not browsing).
Older (pre build 2026.005):
```text
https://sfxota.binbash.rocks:8010
https://sfxota-unstable.binbash.rocks:8010
```
If needed: capture the exact OTA URL from **GOLDEN** via `adb logcat` while
checking for updates — **do not modify GOLDEN** merely for discovery.
---
## 3. Historical IP snapshot (diagnostic only)
Observed **2026-08-09** — **never hard-code**; re-resolve DNS before use:
| Host | IPv4 |
|------|------|
| axpos.org | 217.197.84.141 |
| code.binbash.rocks | 148.251.225.181 |
| download.axpos.org | 148.251.225.182 |
| update.axpos.org | 148.251.225.182 |
| update-unstable.axpos.org | 148.251.225.182 |
| leech.binbash.rocks | 162.55.66.39 |
---
## 4. Existing laptop archive → server first
Laptop working location:
```text
~/Archives/AXP.OS/
website-rendered/
codeberg/
disroot-repositories.jsonl
mirror-forgejo-org.sh
…
```
**Do not** redownload blindly.
Once `rom-dev01` is available:
```bash
ssh android-build@rom-dev01 \
'mkdir -p /srv/rom/references/axp/archive'
rsync -aH --partial --info=progress2 \
"$HOME/Archives/AXP.OS/" \
android-build@rom-dev01:/srv/rom/references/axp/archive/
du -sh ~/Archives/AXP.OS
ssh android-build@rom-dev01 'du -sh /srv/rom/references/axp/archive'
```
Keep the laptop copy until the server copy is verified.
---
## 5. Suggested server layout
```text
/srv/rom/references/axp/archive/
├── website-rendered/
├── codeberg/
│ ├── git/
│ ├── releases/
│ └── metadata/
├── disroot/
│ ├── git/ # sunfish-selected repos only (see §8)
│ ├── releases/
│ └── metadata/
├── github/ # optional APK/release mirrors
├── downloads/
│ ├── sunfish-pro/
│ └── sunfish-slim/
├── status/
├── tools/ # e.g. mirror-forgejo-org.sh
└── README.md
```
Separate from any **working checkouts** used during active porting.
---
## 6. Storage preflight (mandatory)
```bash
df -h /srv/rom
du -sh /srv/rom/references/axp/archive 2>/dev/null || true
```
Preservation **must not** consume space needed for LOS tree, `out/`, ccache,
artifacts.
Full Disroot org (~48 GiB Git + LFS/releases) is **out of scope**. Prefer
sunfish-selected set (typically far smaller) + website + sunfish images.
---
## 7. What to preserve (sunfish-scoped)
### Always (small / high value)
1. Rendered website (`axpos.org`) + `sitemap.xml` / `robots.txt`
2. `axpos-org.git` (Codeberg)
3. Existing Codeberg org archive (rsync; refresh optionally)
4. Disroot org **inventory JSONL** (metadata only)
5. Status probe snapshots under `status/`
### Highest priority if downloads work
Sunfish Pro/Slim OS zip, recovery, factory, `.prop`, `.sha512sum`, public keys
(clearnet path first; Tor fallback).
### Git (Disroot / public) — selection rule
**Do not** clone all 225 repos.
**Do** discover the exact set from **sunfish A15 / 22.2** reproducible-build /
manifest / `axp_versioning` data, then clone **that** set (+ histories).
Minimum explicit interest (seed list — expand from manifests):
| Priority | Examples |
|----------|----------|
| Device/kernel | `android_device_google_sunfish`, `android_kernel_google_msm-4.14` |
| Versioning | `axp_versioning`, sunfish-related manifests / `axp.xml` tags |
| Likely port targets | `android_frameworks_base`, `android_packages_apps_Settings`, shared `build`/config **if** present in sunfish build |
| Compat features | microG / signature spoofing / Phonesky-related public repos |
| Features we want | call recording, FLAG_SECURE-related, hardening **used by sunfish A15** |
Exclude by default: other OEMs/devices, unrelated Pixel generations, bulk
device trees not referenced by the sunfish A15 build.
### Lower priority
GitHub APK/release mirrors for apps we care about (Phonesky, etc.).
### Out of scope
Private `code.binbash.rocks` content without credentials.
---
## 8. Manifest-driven repo selection (preferred workflow)
1. From archived docs / Disroot, locate sunfish **22.2 / A15** reproducible
tags and `axp_versioning` / manifest (`axp.xml` or equivalent).
2. Extract the list of repositories + revisions for that build.
3. Clone `--mirror` **only those** repos (plus any tiny shared deps they need).
4. Record the selection in
`/srv/rom/references/axp/archive/status/sunfish-repo-selection.txt`
with tag/build id and date.
5. `git fsck --full` per mirror; `git lfs fetch --all` where applicable; log LFS
failures separately (404s are expected for some historical objects).
Fallback if manifests are incomplete: inventory API + name filters
(`sunfish`, `msm-4.14`, `phonesky`, `microg`, `signature`, `spoof`, `callrec`,
`versioning`, `manifest`, …) **plus** manual review — still not “clone org”.
---
## 9. Website commands (server refresh)
```bash
mkdir -p /srv/rom/references/axp/archive/website-rendered
wget \
--mirror --page-requisites --convert-links --adjust-extension \
--restrict-file-names=unix --domains=axpos.org \
--directory-prefix=/srv/rom/references/axp/archive/website-rendered \
https://axpos.org/
curl -fL https://axpos.org/sitemap.xml \
-o /srv/rom/references/axp/archive/website-sitemap.xml || true
curl -fL https://axpos.org/robots.txt \
-o /srv/rom/references/axp/archive/website-robots.txt || true
```
Prefer transferring the existing laptop `website-rendered/` and `axpos-org.git`
via rsync; refresh only if needed:
```bash
git --git-dir=axpos-org.git remote update --prune
git --git-dir=axpos-org.git show-ref
git --git-dir=axpos-org.git fsck --full
```
---
## 10. Forgejo inventory (Disroot / Codeberg)
```bash
tmp="$(mktemp)"; page=1
while :; do
data="$(curl -fsSL \
"https://git.disroot.org/api/v1/orgs/AXP.OS/repos?limit=50&page=$page")"
count="$(jq 'length' <<<"$data")"
[ "$count" -eq 0 ] && break
jq -c '.[]' <<<"$data" >> "$tmp"
page=$((page + 1))
done
cp "$tmp" /srv/rom/references/axp/archive/disroot/metadata/disroot-repositories.jsonl
rm "$tmp"
```
Useful queries: largest repos; name filter for sunfish-interest (see research
notes). Same pattern for Codeberg org `AXP-OS`.
If using `mirror-forgejo-org.sh` from the laptop archive:
- **Codeberg**: OK to refresh the small org into `archive/codeberg/`
- **Disroot**: do **not** invoke a full-org mirror by default; use
manifest-selected clones into `archive/disroot/git/`
Scripts must be resumable; one repo failure must not abort the batch; prefer
`git remote update --prune` over delete/reclone.
---
## 11. Service / path probes
Probe roots **and** exact sunfish paths (`/axp/sunfish/`, `/axp-slim/sunfish/`).
Save under `archive/status/` with `date -Is`.
Tor fallback (optional):
```bash
# apt: tor torsocks — only if needed
curl --socks5-hostname 127.0.0.1:9050 --connect-timeout 30 --max-time 120 -I \
'http://e3tx35xwvqoihx36tjmnzurjcghs6kjwrwnen55jg7ujqilbaicutpid.onion:8008/axp/sunfish/'
```
Mine archived website for URLs:
```bash
grep -RhoE 'https?://[^"<> ]+' \
/srv/rom/references/axp/archive/website-rendered/axpos.org 2>/dev/null |
grep -Ei 'axpos|binbash|codeberg|disroot|github|sunfish' | sort -u \
> /srv/rom/references/axp/archive/discovered-urls.txt
```
---
## 12. Verification
```bash
find /srv/rom/references/axp/archive -type d -name '*.git' -prune | wc -l
du -sh /srv/rom/references/axp/archive/*
date -Is > /srv/rom/references/axp/archive/SNAPSHOT_DATE.txt
```
Document LFS/release errors. Do not claim 100% completeness if objects 404.
---
## 13. Priority order (execution)
1. rsync laptop `~/Archives/AXP.OS/` → server; verify `du`
2. Storage preflight (`df`)
3. Website rendered + `axpos-org.git` (if not already complete)
4. Probe/download **sunfish** Pro/Slim images + checksums/keys (clearnet → Tor)
5. Disroot inventory JSONL + **manifest-driven** sunfish repo mirrors
6. Optional Codeberg refresh; optional GitHub APK repos of interest
7. Status snapshot + selection file
Skip: full Disroot org; private binbash Gitea; endless LFS 404 retries.
---
## 14. Later development use
Do **not** blindly cherry-pick AXP into LOS 23.2.
For each candidate:
1. Identify repo + AXP branch/tag/build (prefer sunfish 22.2 / A15)
2. Identify corresponding LOS/AOSP base
3. Inspect full diff
4. Classify: direct / port / obsolete / superseded / A16-incompatible
5. Record provenance; dedupe vs LOS / GrapheneOS / DivestOS
6. Apply one logical change; build; test on **LAB** only
AXP A15 GOLDEN remains the live oracle.
LOS 23.2 / Android 16 remains the base.
+100
View File
@@ -0,0 +1,100 @@
# ChatGPT conversation continuity
Imported context: 2026-09-13 from [mi ROM](https://chatgpt.com/share/6aa718c6-c834-83ed-9e2d-2b18fc1ad141).
This is a non-normative continuity note, not a replacement for the constitution
or feature specs. The shared page was retrieved and its user/assistant text
extracted. The payload contains repeated messages; extraction order is not a
reliable chronology. Linked attachments, redacted tool outputs and the original
external citations have not been independently recovered or verified. Historical
assistant suggestions are not new user instructions or verified technical facts.
The raw conversation is not copied into Git because it includes unrelated
personal topics and diagnostic data.
## Intent and settled direction
- Build a personal, maintainable ROM for **Pixel 4a / sunfish only**, with
LineageOS 23.2 / Android 16 as the base and AXP.OS Pro as the functional
reference. The motivation is continuity beyond AXP maintenance.
- Functionality comes first, especially microG, patched Google Play and banking
inside Insular. Yuh and Swissquote are the required targets; N26 and Revolut
were optional challenges, not reasons to block the project.
- Preserve two-device separation: GOLDEN is the daily AXP reference; LAB is for
experiments. The conversation corrected an earlier A13 mix-up: GOLDEN is AXP
A15; historical GrapheneOS sunfish is A13. Never revive the earlier assumption
that there is only one phone or that GOLDEN must be wiped/restored.
- Keep the compatible LineageOS kernel/device stack. Study historical sunfish
GrapheneOS, modern applicable GrapheneOS techniques, and AXP/DivestOS changes;
do not transplant an old GrapheneOS or stock A13 kernel into the A16 ROM.
- Rootless compatibility comes first (`006`), then KernelSU-Next (`007`) with
a complete banking regression. Magisk describes the existing AXP workflow,
not an intermediate production architecture for the new ROM.
- Native call recording and configurable FLAG_SECURE override matter for daily
use. Prefer integrating these into the ROM over depending on root modules.
- Insular starts as a user app; Managed Mainland is optional. Preserve the
historical Aurora → do not launch → deny root → Insular first launch → Play
update workflow as reference, with exact current behaviour measured in `002`.
- NeoBackup was accepted as an external personal tool. A custom TWRP-like full
backup implementation is not part of the ROM project.
- Signing, AVB/relocking and OTA remain later release work. The conversation
raised hardware attestation explicitly: do not promise Play Integrity or
hardware-attestation success merely because the ROM has KernelSU or custom
signing. Validate actual banking behaviour and research current requirements
when that feature is implemented.
## Additional research context
The last Phonesky discussion considered module-based installers, but retained
the AXP-style patch/build/sign pipeline as the intended direction. It identified
`https://github.com/AXP-OS/packages_apps_phonesky` as a research lead for the
patcher, versioned diffs and release automation. Revalidate upstream before use;
do not treat the historical version or maintenance claims as current facts.
Stable signing identity and subsequent APK updates belong in feature `004`.
Privacy candidates included SUPL identifiers, Build.SERIAL restrictions,
package visibility, MAC privacy, analytics removal, hosts/DNS filtering,
certificate review, auto-reboot, radio timeouts and log reduction. Preserve them
through [the existing backlog](hardening-backlog.md), promoting requirements
into specs before implementation.
## Working preferences and infrastructure
The user prefers Spanish conversation, concrete next steps and manageable stages;
large speculative plans and repeated reopening of settled decisions are tiring.
Technical project documentation remains English.
The laptop has limited storage. Android trees, builds and growing reference
archives belong on `rom-dev01`; local work centres on editing, SSH, adb/fastboot
and artifact transfer. The agreed planning capacity was **6 vCPU, 52 GB RAM,
32 GB swap and approximately 2 TB expandable disk**. The chat explicitly
corrected assistant claims of 16 vCPU/64 GB/750 GB and confusion between laptop
free space and server capacity. Actual host measurements still take precedence.
Keep independent Gitea repositories, Ansible and Semaphore; do not fold this
personal project into company GitLab, Redmine or Salt workflows. VM provisioning
remains outside this repository. Public IP, public web access, SSH tunnels and
possible future WireGuard were discussed separately. That discussion is not
authorization to expose services: current constitution/spec access rules govern.
AXP preservation is scoped to sunfish and relevant shared dependencies, not all
225 Disroot repositories or every GrapheneOS archive. Transfer and verify the
existing laptop archive before redundant downloads. Historical LFS failures and
service outages are observations, not proof of current availability. Existing
[reference sources](reference-sources.md) and
[preservation instructions](axp-sunfish-preservation.md) already capture this.
## Current repository supersedes historical waiting state
The chat ended its planning phase waiting for `rom-dev01` before implementing
`001`. That is historical context, not today's task status. Repository records
dated 2026-09-13 already describe host work, the `002` handoff, and `003` source
initialization. See [002 handoff](002-gate-handoff.md) and
[003 build notes](003-build-notes.md); live execution status was not checked
during this context import.
In particular, `002` now documents a Yuh failure on GOLDEN. The earlier statement
that both banks worked is a historical observation, not a substitute for the
current inventory or a reason to mark compatibility passed.
Resume from current feature evidence and tasks, preserving this conversation's
intent without replaying old commands or overwriting newer decisions.
+35
View File
@@ -0,0 +1,35 @@
# Device roles — two Pixel 4a (sunfish)
Project assumption: **two physical sunfish devices**.
| Role | Use | Flash / experiment? |
|------|-----|---------------------|
| **GOLDEN** | Daily-driver **AXP.OS Pro 22.2 / Android 15** (main phone) | **NO** — never flash/wipe/modify; **`adb` sample only** |
| **LAB** | Experiments / our ROM (as found: **iodé** / Lineage 7.8 A16) | **YES** — flash freely; factory rescue ready |
## Measured GOLDEN (2026-09-13)
| Field | Value |
|-------|--------|
| Serial | `11071JEC203839` |
| Build | `22.2-2026.003-sunfish-pro` (`ro.lineage.releasetype=PRO`), Android **15**, patch `2026-03-03` |
| Bootloader | **unlocked** (`flash.locked=0`, `verifiedbootstate=orange`) |
| Magisk | **29.0** (`/system_ext/bin/magisk`) |
| Work profile | Island user 10; Yuh + Swissquote in Island only |
| Inventory | [`002-golden-inventory.md`](002-golden-inventory.md) |
## Measured LAB (2026-09-13)
| Field | Value |
|-------|--------|
| Serial | `11231JEC207477` |
| As found | iodé / `7.8-20260902-sunfish`, Android **16**, patch `2026-08-01` |
| Bootloader | **locked** |
| Baseline note | [`002-lab-iodes-baseline.md`](002-lab-iodes-baseline.md) |
| Factory rescue | [`002-lab-stock-rescue.md`](002-lab-stock-rescue.md) — SHA-256 **VERIFY_OK** |
## Implications
- NeoBackup / wipe of GOLDEN is **not** a project requirement.
- `003+` flashing **only on LAB**.
- **Do not flash GOLDEN** under any circumstance.
+119
View File
@@ -0,0 +1,119 @@
# Hardening backlog (non-normative)
Candidate hardening ideas for later evaluation. **Not requirements** until a row
is promoted into an active feature `spec.md` (`012` / `013` or related).
Authority order for an active feature:
1. Constitution
2. That feature's `spec.md`
3. This backlog / `docs/axp-pro-parity.md` (hints only)
Status: `candidate` | `investigate` | `accepted→spec` | `rejected` | `los-provides`
## Privacy / identifiers
| Idea | Notes | Status |
|------|-------|--------|
| SUPL IMSI/MSISDN exposure | Reduce carrier ID leakage via SUPL | candidate |
| Build.SERIAL restrictions | Identifier privacy | candidate |
| Analytics / statistics removal | Drop unused telemetry | candidate |
| Package query visibility | Tighten package visibility | candidate |
| MAC / address privacy | Wi-Fi/BT randomization policy | candidate |
| Private DNS defaults | Sensible defaults without breaking banks | candidate |
| CA-store review | Trust anchors audit | candidate |
## Userspace
| Idea | Notes | Status |
|------|-------|--------|
| hardened_malloc | Graphene/AXP portability on sunfish | candidate |
| Bionic hardening | Selective, attributable | candidate |
| JNI protections | Safer JNI boundaries | candidate |
| Stack canaries | Confirm / strengthen where applicable | candidate |
| ptrace / Yama | Restrict debugging attach | candidate |
| seccomp | Incremental filter improvements | candidate |
| noexec / nosuid / nodev mounts | Where applicable without breaking HW | candidate |
| Dynamic code / JIT restrictions | Evaluate app compatibility cost | candidate |
| Exec spawning / ASLR-related | Attack-surface reduction | candidate |
## Kernel
| Idea | Notes | Status |
|------|-------|--------|
| Historical GrapheneOS sunfish kernel patches/config | Device-specific commits **and** config known to have run on sunfish — not CONFIG-only review | candidate |
| Modern GrapheneOS kernel techniques | Evaluate equivalent/backport on sunfish 4.14; no blind cherry-picks | candidate |
| AXP/DivestOS sunfish kernel patch audit | Identify AXP-owned vs inherited vs upstream; de-duplicate against LOS | candidate |
| Applicable CVE backports | Document EOL firmware vs patchable kernel | candidate |
| CFI | Feasibility on 4.14 | candidate |
| ASLR config | Kernel/userspace related knobs | candidate |
| IPv6 privacy | Address generation / exposure | candidate |
| Debug / UART restrictions | Reduce attack surface | candidate |
| Attack-surface reduction | Drop unused drivers/configs carefully | candidate |
## Kernel hardening source policy
The kernel base remains the LineageOS 23.2-compatible sunfish kernel.
```text
┌─ historical GOS sunfish
│
LOS23/A16 sunfish 4.14 ←─┼─ AXP own + DivestOS
OUR BASE │
├─ modern GOS techniques
│
└─ original upstream fixes
```
**Not:** GrapheneOS Android 13 (or any old GrapheneOS) kernel tree as our base.
Candidate hardening comes from four classes:
1. **Historical GrapheneOS sunfish** — device-specific patches/configuration known
to have run on sunfish (real commits, not only `.config`).
2. **AXP.OS / DivestOS sunfish** — audit the actual patch stack; distinguish
AXP-owned changes from inherited changes.
3. **Modern GrapheneOS** — use current kernel hardening as a source of
techniques/security properties; adapt/backport only where feasible on
non-GKI Linux 4.14; do not assume current Pixel kernel patches cherry-pick
cleanly.
4. **Original upstream** — where a GrapheneOS/AXP/Divest patch derives from AOSP,
Android common, Linux stable, or another upstream source, prefer the
appropriate original upstream fix when practical.
For each candidate record:
- security problem / property;
- original source and commit;
- source lineage / provenance;
- sunfish-specific or generic;
- already present in LOS 23.2?;
- already present through another patch?;
- kernel/hardware prerequisites;
- applicability to Linux 4.14;
- port/backport required;
- compatibility/regression risk;
- test result;
- final decision.
## Much later / evaluate
| Idea | Notes | Status |
|------|-------|--------|
| Per-app network control | High UX/complexity | candidate |
| Sensors permission model | Graphene-like | candidate |
| Storage Scopes-like behaviour | Compatibility cost | candidate |
| Contact Scopes-like behaviour | Compatibility cost | candidate |
| Duress PIN/password | Later | candidate |
| USB restrictions | Policy toggles | candidate |
| Exploit-protection toggles | User-facing switches | candidate |
| Modernize LOS API deprecations / Kotlin annotation targets | **Final-ROM hygiene (last steps)**, not a mid-feature gate. Measured on `005` bacon 2026-09-15 SUCCESS (~45k log lines): ~3k deprecated, ~4.5k KT-73255 annotation-target, ~8.7k “warning” hits total; **0** hard `error:`/`FAILED:` in successful run. Themes: Settings Slices `[removal]`; SystemUI KeyguardState/SceneContainer/CommunalScenes; DesktopMode desk APIs; `FingerprintManager`/`Executors`; SettingsLib `@StringRes` KT-73255. Fix **API-by-API** with attributable ports; never megapatch or `-Xlint:-deprecation` as “done”. Prefer waiting for LOS merges; only port what still warns at freeze. Own patches (`005` spoofing, vendor) must stay clean first. Evidence: `/srv/rom/artifacts/005-spoofing/20260915-bacon/bacon-log-triage.md`. | candidate |
## EOL firmware caveat (for `013`)
sunfish is EOL. The project can modify kernel/framework we build, but cannot
fabricate new proprietary vendor/modem firmware. Document clearly:
- security patches we can backport in-tree;
- old proprietary firmware/baseband/vendor images we **cannot** fix;
- do not claim hardware protections unsupported by Pixel 4a.
+365
View File
@@ -0,0 +1,365 @@
# Reference sources — LineageOS, AXP.OS, GrapheneOS (sunfish only)
Project documentation consolidating where sources live, what role they play, what
is already archived locally, and what to execute later on `rom-dev01`.
**Scope: Google Pixel 4a (`sunfish`) only.** Do not plan or archive unrelated
devices (other Pixels, OnePlus, Fairphone, Sony, …).
```text
LineageOS 23.2 / Android 16 → AUTHORITATIVE CURRENT BASE (build from this)
AXP.OS 22.2 / Android 15 → GOLDEN / functional reference (live device + selective archive)
GrapheneOS Android 13 sunfish → HISTORICAL hardening reference (selective archive)
```
Do **not** execute server archive/download/build commands from this file until
`rom-dev01` exists. Do **not** treat this as work on frozen feature `001`.
Detailed AXP hosting probes, Tor/download URLs, and Forgejo inventory commands:
[`axp-sunfish-preservation.md`](axp-sunfish-preservation.md).
Device roles (GOLDEN vs LAB): [`device-roles.md`](device-roles.md).
---
## Classification legend
| Label | Meaning |
|-------|---------|
| **AUTHORITATIVE CURRENT SOURCE** | Use for builds / official current truth |
| **HISTORICAL REFERENCE** | Study/port only; not the build base |
| **LOCAL ARCHIVE** | Copy under `/srv/rom/references/…` or laptop path |
| **UNAVAILABLE / PRIVATE** | Do not scrape or bypass access controls |
| **TO BE EXECUTED LATER ON rom-dev01** | Commands/actions deferred until host is ready |
---
## Target layout on rom-dev01
```text
/srv/rom/references/
├── axp/
│ ├── archive/ # see axp-sunfish-preservation.md
│ ├── manifests/ # sunfish A15 selection / versioning notes
│ └── README.md
├── lineage/
│ ├── manifests/ # repo manifest -r snapshots (reproducibility)
│ └── README.md
└── grapheneos/
├── mirrors/ # selective bare clones only
├── manifests/
├── metadata/
└── README.md
```
Plus the normal **build checkout** (not an “archive”):
```text
/srv/rom/src/lineage-23.2/ # full Lineage tree used to compile (feature 003+)
```
---
# 1. LineageOS 23.2 / Android 16 — BASE
## Role
**AUTHORITATIVE CURRENT SOURCE** for the ROM. Compiling = syncing and building
this tree for sunfish. No need for a redundant mass mirror of the entire
LineageOS GitHub organization.
## Authoritative URLs
| Item | URL | Class |
|------|-----|--------|
| Android / repo umbrella | https://github.com/LineageOS/android | AUTHORITATIVE CURRENT |
| Device sunfish | https://github.com/LineageOS/android_device_google_sunfish | AUTHORITATIVE CURRENT |
| Kernel msm-4.14 | https://github.com/LineageOS/android_kernel_google_msm-4.14 | AUTHORITATIVE CURRENT |
| Wiki device page | https://wiki.lineageos.org/devices/sunfish/ | AUTHORITATIVE CURRENT (verify against repos) |
**Note:** Indexed/cached wiki install pages may still mention older branches
(e.g. 22.2). For branch and source tree truth, trust **current GitHub branches /
official `repo init`**, not a stale wiki cache. As of project research,
`android_device_google_sunfish` and `android_kernel_google_msm-4.14` expose
**`lineage-23.2`**.
## Official source init
```bash
repo init \
-u https://github.com/LineageOS/android.git \
-b lineage-23.2 \
--git-lfs
repo sync
```
(Exact path on host: e.g. `/srv/rom/src/lineage-23.2` — set during `003`.)
## What to preserve locally (not a full org archive)
**TO BE EXECUTED LATER ON rom-dev01** after syncs/builds:
```bash
mkdir -p /srv/rom/references/lineage/manifests
cd /srv/rom/src/lineage-23.2 # or actual checkout path
# After first successful sync:
repo manifest -r \
-o /srv/rom/references/lineage/manifests/lineage-23.2-initial.xml
# After first known-good vanilla sunfish boot/build:
repo manifest -r \
-o /srv/rom/references/lineage/manifests/lineage-23.2-sunfish-vanilla-known-good.xml
```
Also keep short README under `/srv/rom/references/lineage/README.md` with:
- `repo init` command used
- date / host
- pointer to known-good images under `/srv/rom/artifacts/` (hashes)
## Explicitly out of scope for Lineage “archive”
- `git clone --mirror` of hundreds of LineageOS repos “just in case”
- Mirroring unrelated device trees
The build checkout **is** the working copy; revision-locked manifests are the
reproducibility archive.
---
# 2. AXP.OS 22.2 / Android 15 — GOLDEN / functional reference
## Role
**HISTORICAL + FUNCTIONAL REFERENCE** (and live GOLDEN device). Not the build
base. Infrastructure risk is higher (self-hosted pieces may vanish) → selective
archive matters more than for Lineage.
Full operational cookbook: [`axp-sunfish-preservation.md`](axp-sunfish-preservation.md).
## Source classes
| Source | Class | Notes |
|--------|-------|--------|
| GOLDEN phone (AXP 22.2 / A15) | AUTHORITATIVE CURRENT *for behaviour* | Live `adb`; do not flash |
| https://axpos.org (docs/news/devices) | AUTHORITATIVE CURRENT *docs* (while up) | Hosted via Codeberg |
| https://git.disroot.org/AXP.OS | AUTHORITATIVE CURRENT *public git* | Main public mirror (~225 repos; **do not clone all**) |
| https://codeberg.org/AXP-OS | HISTORICAL / PARTIAL | Website git + fewer repos after Jan 2026 removals |
| https://github.com/AXP-OS | AUTHORITATIVE for some **APK/releases** | Lower priority mirror |
| https://code.binbash.rocks/AXP.OS | UNAVAILABLE / PRIVATE anonymously | Alive; no anonymous repo list; do not bypass |
| https://download.axpos.org/…/sunfish/ | AUTHORITATIVE downloads (when reachable) | Probe exact paths |
| Tor onion `:8008` | FALLBACK downloads | See preservation doc |
| `leech.binbash.rocks` | UNAVAILABLE (2026-08-09 probes) | Legacy |
## Sunfish-only archive criteria
Preserve/reference **only**:
- documentation/web useful to the project
- `android_device_google_sunfish`, `android_kernel_google_msm-4.14`
- manifests / `axp_versioning` needed to identify exact AXP sunfish revisions
- common repos **only when** they contain AXP changes applicable to sunfish
(e.g. frameworks/base, Settings, build/config, microG/spoofing, Phonesky,
callrec, FLAG_SECURE, hardening) — **selected via sunfish A15 manifests**,
not by cloning the whole org
- sunfish Pro/Slim builds, recovery, factory, `.prop`, checksums, public keys
where recoverable
**Discarded:** full Disroot “225 repos / ~48 GiB Git” org mirror as a project
goal.
## LOCAL ARCHIVE (laptop today)
```text
~/Archives/AXP.OS/
website-rendered/
codeberg/ # ~14 repos / ~38 GiB; LFS gaps in legacy_prebuilt_apps
disroot-repositories.jsonl
mirror-forgejo-org.sh
…
```
## TO BE EXECUTED LATER ON rom-dev01
1. rsync laptop archive → `/srv/rom/references/axp/archive/`
2. `df` preflight (must not steal LOS/`out`/ccache space)
3. Refresh website / selected Disroot mirrors via **manifest-driven** selection
4. Probe/download sunfish Pro/Slim artifacts if hosts allow
5. Never attempt private binbash access
See [`axp-sunfish-preservation.md`](axp-sunfish-preservation.md) for commands.
---
# 3. GrapheneOS — Android 13 sunfish (historical hardening)
## Role
**HISTORICAL REFERENCE** for hardening techniques/patches that once ran on
sunfish. **Not** the ROM base. Official project: sunfish **unsupported**; old
trees moved toward **GrapheneOS-Archive**.
Confirms project rule: GrapheneOS sunfish = **A13 history**, never A16 base.
## Authoritative / archive URLs
| Item | URL | Class |
|------|-----|--------|
| Current GrapheneOS orgs | https://github.com/GrapheneOS | AUTHORITATIVE CURRENT (active devices) |
| Platform manifest (current) | https://github.com/GrapheneOS/platform_manifest | AUTHORITATIVE CURRENT |
| Official historical archive org | https://github.com/GrapheneOS-Archive | HISTORICAL REFERENCE |
| sunfish device (archived) | https://github.com/GrapheneOS-Archive/device_google_sunfish | HISTORICAL — archive/read-only (e.g. since 2025-12-04); branch **13** |
| Releases / changelog | https://grapheneos.org/releases | AUTHORITATIVE for release history (A13 era mentions Pixel 4a) |
| FAQ | https://grapheneos.org/faq | AUTHORITATIVE docs |
| Example reorganized tree | `GrapheneOS-Archive/platform_frameworks_base-old` (read-only) | HISTORICAL — illustrates repo moves |
**Do not** mirror all ~244 repos in GrapheneOS-Archive.
## Selection rule (same spirit as AXP)
1. From last sunfish **Android 13** GrapheneOS manifests/tags, list repos/commits
that carried GrapheneOS deltas relevant to sunfish.
2. Mirror **only** those (plus `device_google_sunfish` + useful manifests).
3. Likely candidates after proof: frameworks, system/core, SELinux, malloc,
kernel/configs, etc. — **only if** tied to sunfish A13 lineage.
4. Record provenance; port carefully to LOS 23.2 / 4.14 (see constitution /
`docs/hardening-backlog.md`).
## TO BE EXECUTED LATER ON rom-dev01 (minimum seed)
```bash
mkdir -p /srv/rom/references/grapheneos/{mirrors,manifests,metadata}
git clone --mirror \
https://github.com/GrapheneOS-Archive/device_google_sunfish.git \
/srv/rom/references/grapheneos/mirrors/device_google_sunfish.git
git clone --mirror \
https://github.com/GrapheneOS/platform_manifest.git \
/srv/rom/references/grapheneos/mirrors/platform_manifest.git
git --git-dir=/srv/rom/references/grapheneos/mirrors/device_google_sunfish.git fsck --full
git --git-dir=/srv/rom/references/grapheneos/mirrors/device_google_sunfish.git show-ref
```
Then expand selectively from A13 sunfish manifests/tags — not the whole Archive
org.
## LOCAL ARCHIVE
No complete GrapheneOS sunfish archive is assumed on the laptop yet (unlike
AXP). Seed mirrors are **TO BE EXECUTED LATER ON rom-dev01**.
---
# 4. Conceptual comparison
| | LineageOS | AXP.OS | GrapheneOS (sunfish) |
|--|-----------|--------|----------------------|
| Android | 16 (23.2) | 15 (22.2) | 13 (last) |
| Role | **BASE** | **GOLDEN / functional** | **Hardening history** |
| Full tree checkout | Yes (to build) | No (selective) | No (selective) |
| Mass org mirror | No | No | No |
| Key local artifact | `repo manifest -r` XML | web + sunfish repos + images | device_sunfish + selected A13 repos |
| Urgency | Low (GitHub active) | Higher (self-host risk) | Medium (already moving to Archive) |
---
# 5. Shared porting discipline
For **any** AXP or GrapheneOS candidate change:
1. Identify source repo + tag/branch/build (sunfish-relevant)
2. Identify corresponding LOS 23.2 / AOSP base
3. Inspect full diff
4. Classify: direct / port / obsolete / superseded / A16- or 4.14-incompatible
5. Deduplicate vs LOS / the other reference
6. One logical change; build; test on **LAB** only
---
# 6. Execution status checklist
| Item | Status |
|------|--------|
| Document Lineage URLs + manifest `-r` strategy | Done (this file) |
| Document AXP sunfish-scoped preservation | Done (`axp-sunfish-preservation.md` + this file) |
| Document GrapheneOS Archive sunfish strategy | Done (this file) |
| Laptop AXP archive under `~/Archives/AXP.OS/` | Exists (operator); rsync later |
| rom-dev01 AXP/Lineage/GOS archives | **TO BE EXECUTED LATER** |
| Lineage full sync / first `manifest -r` | **TO BE EXECUTED LATER** (`003`) |
| Feature `001` implement | Not started (frozen docs) |
| Features `002`/`003` | Not started |
---
# 7. Contradictions / pitfalls noted
1. **Wiki vs GitHub for Lineage:** install wiki caches may show 22.2; repos show
`lineage-23.2` — trust repos/`repo init`.
2. **AXP “~48 GiB Disroot” ≠ required archive size** — that is whole-org Git
metadata; project policy is sunfish-selected only.
3. **Codeberg AXP archive ≠ complete** — LFS 404s (e.g. `legacy_prebuilt_apps`);
keep anyway; do not claim 100% LFS.
4. **download.axpos.org `/` timeout ≠ paths dead** — probe `/axp/sunfish/` etc.
5. **OTA `/` → 403** does not mean updater API is down.
6. **GrapheneOS-Archive growth** — sunfish material is being archived; prefer
Archive URLs + manifests over assuming old live GrapheneOS device support.
7. **AXP Android version:** GOLDEN is **22.2 / A15**; last GrapheneOS sunfish is
**A13** — do not conflate.
---
# 8. Community inspiration (non-base) — LOS + microG + KSU-Next + SusFS
**Not** our ROM base. Useful field report for sunfish banking / root / SusFS.
| Item | Detail |
|------|--------|
| Thread | [XDA: UNOFFICIAL sunfish LOS 22.2 + MicroG + KSU-Next + SusFS](https://xdaforums.com/t/rom-unofficial-sunfish-pixel-4a-lineageos-22-2-microg-ksu-next-susfs.4772261/) (torukk) |
| Ship | LOS **22.2** / A15 + upstream microG + kernel KSU-Next + SusFS |
| Kernel tree (author) | https://github.com/abu-adm/android_kernel_google_msm-4.14 (branch noted compatible with LOS23 kernel work) |
| Claims | Revolut / hard banks workable with **spoofed** KSU-Next manager + SusFS + **TrickyStore** + microG “unblock hardware attestation”; integrity landscape changes fast |
| LOS 23.2 note (forum) | Boot image with SusFS/ReSukiSU flashed onto official LOS 23.2: SusFS works, but ReSukiSU config unstable, heat, sudden reboots vs Magisk |
| Our use | Inspiration for `006`/`007` (profiles, SusFS, attestation modules on **LAB**). Still build **our** LOS 23.2 vanilla first (`003`), then microG/Phonesky, then KSU-Next. Do **not** adopt as drop-in base; layer AXP/Graphene call-rec / FLAG_SECURE / hardening later as planned |
If iodé/vanilla keep failing Yuh KeyMint/RKP, this thread’s stack (SusFS + TrickyStore + microG HA toggles) is the first **LAB** experiment family to evaluate — after stock factory diagnosis when convenient.
---
# 9. Community inspiration (non-base) — rooted GrapheneOS OTAs
**Not** our ROM base. **Not** for sunfish (EOL; project targets modern supported Pixels).
| Item | Detail |
|------|--------|
| Repo | https://github.com/schnatterer/rooted-graphene |
| Idea | Patch **Magisk** into official GrapheneOS OTAs; keep **AVB + locked bootloader** + root; own OTA channel via [Custota](https://github.com/chenxiaolong/Custota) / GitHub Pages; switch Magisk ↔ rootless by OTA |
| Tooling of interest | [avbroot](https://github.com/chenxiaolong/avbroot), Custota, signed OTA pipeline |
| Limits (upstream notes) | Stock Magisk Zygisk broken in this setup → banking detection; optional third-party Magisk fork; **KernelSU hard to integrate** with this Magisk-in-OTA approach |
| Our use | Inspiration for later **`011`** (project signing / AVB / OTA) and for “locked + root” trade-offs — **not** a Magisk production path (constitution: KernelSU-Next). Do not treat as GrapheneOS-on-sunfish revival |
Contrast with §8 (torukk): that one is **sunfish + KSU-Next + SusFS + LOS**; this one is **modern Graphene + Magisk + locked AVB OTA**. Different lessons for different stages.
---
# 10. Attestation / banking compatibility references
Primary reading when designing `006`/`007` attestation strategy (not sunfish-specific
fixes by themselves):
| Source | URL | Why keep |
|--------|-----|----------|
| GrapheneOS attestation compatibility guide | https://grapheneos.org/articles/attestation-compatibility-guide | Explains **hardware attestation API** vs Play Integrity; `SelfSigned` + whitelist of official GOS verified-boot keys (`attestation.json`); why modern supported Pixels can work with banks that whitelist GOS; sunfish **not** on that key list (EOL) |
| PrivSec banking apps compat report (issue) | https://github.com/PrivSec-dev/banking-apps-compat-report/issues/509 | Crowdsourced banking/attestation outcomes across ROMs (incl. Graphene); use for comparative evidence, not as a recipe dump |
**Project takeaway:** Yuh/Swissquote working on **supported** GrapheneOS tracks
whitelist + healthy TEE/AVB on current Pixels. Our sunfish path still needs
LOS+microG/Phonesky + measured KeyMint/RKP behaviour; GOS docs inform *what*
apps check, not a drop-in port.
+87
View File
@@ -0,0 +1,87 @@
# Session handoff — 2026-09-16 (LAB / 007)
**For the next chat:** read this file first, then `docs/007-kernelsu-runbook.md`.
**Prior chat (full context):** Cursor transcript id
`a601104c-c593-45d3-b737-545a7838957d`
(path: `~/.cursor/projects/home-aventura-Workspaces-sunfish-a16-rom/agent-transcripts/a601104c-c593-45d3-b737-545a7838957d/`).
**Rules:** working tree only (no commit / push / MR). LAB only
`11231JEC207477` — never GOLDEN.
---
## Project spine (do not forget)
We did **not** start from abu or from LOS 22 as the product base.
1. **`003`** — LOS **23.2** vanilla on LAB
2. **`004`/`005`** — microG + patched Play (Phonesky) + restricted spoofing → bacon zip
3. **`007`** — our ReSukiSU root on **our** `msm-4.14`, then modules / SusFS / banks
**ROM zip (ours):**
`.local-artifacts/005-spoofing/20260915-bacon/lineage-23.2-20260915-UNOFFICIAL-sunfish.zip`
**abu / torukk / Downloads `boot-sunfish-resukisu*.img`:** reference only (SusFS / module ideas).
**Never** treat Downloads abu boots as “ours”.
---
## Our boot images (host + laptop)
| Role | File | Where |
|------|------|--------|
| **Penúltimo (known-good ReSukiSU, no SusFS)** | `boot-resukisu-our-20260915T183729Z.img` | Host `/srv/rom/artifacts/007-kernelsu/` (+ symlink `boot-resukisu-our-latest.img`). Copy to laptop before flash if missing locally. |
| **Último (ReSukiSU + SusFS API bridge)** | `boot-resukisu-susfs-20260916T004038Z.img` | Host + laptop `.local-artifacts/007-kernelsu/boot-resukisu-susfs-latest.img` — sha256 `455a77a6…` |
Earlier same-day builds: `…T172342Z` / `…T172400Z` (superseded by `T183729Z`).
---
## What happened today (facts)
### Kernel / SusFS (host)
- Integrated **ReSukiSU** (not KSU-Next) into our tree; fixed shallow-clone bogus `KSU_VERSION=30701`.
- Built SusFS after bridging non-GKI 4.14 SusFS (`INODE_STATE_*`, etc.) with ReSukiSU GKI-style ioctl `CMD_*` / wrappers — compile+link OK (~18m).
- Pin notes: `.local-artifacts/007-kernelsu/susfs-pin/` (host tree under `kernel/google/msm-4.14`).
- **Process note:** SusFS build ran before US2 hide verification (constitution prefers profiles first). Treat SusFS boot as **trial**, not claimed.
### LAB flash chaos (evening)
- Torukk **22.2 MicroG** was on device temporarily (test/capture); **not** our product path.
- Attempted restore to **our 23.2 bacon** + SusFS boot.
- `fastboot -w` erased userdata **without** formatting (`File system type raw not supported`) → “Can't load Android system / data may be corrupt”.
- Wrong boot paths (`~/Downloads/boot-sunfish-resukisu.img`) = **abu/torukk**, not ours — caused confusion.
- Sideload of our 23 zip from old recovery: signature / **downgrade** prompts (recovery compares timestamps/patch, not “22&lt;23” marketing). Accept install-anyway / downgrade when intentional.
- End of session: operator reported **sideload of our 23 finished and device booted**. Confirm with:
`adb shell getprop ro.lineage.version` → expect `23.2-20260915-…`.
- Next flash after that sideload: **our penúltimo** boot (`boot-resukisu-our-20260915T183729Z`), then later trial SusFS if needed.
### What is *not* done
- SusFS boot **not** proven stable on our 23.2 (earlier bootloop / corrupt-data episode with SusFS + messy slots).
- US2 hide verification, modules (TEESimulator-RS / Zygisk / PIF), Island banks — still open.
- microG **Location UI** gap on our 23.2 (see urgent next).
---
## Urgent next (new chat)
1. **Confirm LAB identity:** `ro.lineage.version` = our 23.2 bacon; slot; serial `11231JEC207477`.
2. **Flash our penúltimo boot** (if not already):
`fastboot flash boot …/boot-resukisu-our-20260915T183729Z.img` then reboot.
3. **URGENT — Location options (`005`/`004` follow-up):**
On `23.2-20260915` + microG 0.3.16, operator saw **no usable microG Location UI** (no remember-from-GPS / request-from-online / hotspot-style controls vs richer UI on community 22.2).
Re-check on restored stack: screenshots + `dumpsys`; decide if packaging/overlay/settings gap.
Docs: `docs/004-lab-selfcheck.md` § “LAB observation (follow-up — 2026-09-16)”.
4. Reinstall manager (matching `KSU_VERSION`) + modules; resume `007` US2 → SusFS trial → attestation / Yuh+Swissquote.
5. Do **not** re-introduce abu ROM/kernels into the flash path; keep abu trees as SusFS/reference only.
---
## Spec / doc touchpoints updated this handoff
- `docs/007-kernelsu-runbook.md` — our boots, SusFS trial status, abu archived as reference
- `specs/007-kernelsu-sunfish/tasks.md` — T018 build done; T019 pending proof
- `specs/README.md` — `007` active
- `docs/004-lab-selfcheck.md` — Location marked urgent for return to our stack
@@ -0,0 +1,228 @@
sunfish-a16-rom — Spec Kit beginner guide + pre-plan briefing for ChatGPT
Generated: 2026-08-09
Project path: /home/aventura/Workspaces/sunfish-a16-rom
Constitution: 0.1.5
Active feature (.specify/feature.json): specs/001-development-environment
================================================================================
1) WHAT HAPPENED WHEN WE "CREATED" THE PROJECT
================================================================================
In this project we did NOT use the SSYSTEMS command /speckit.project-create.
We used upstream Spec Kit:
specify init --here --integration cursor-agent --script sh
That command:
- created .specify/ (scripts, templates, workflows, memory/constitution template)
- installed Cursor skills under .cursor/skills/speckit-*
- those skills are what appear in Cursor as /speckit-plan, /speckit-tasks, etc.
So yes: initializing a Spec Kit project is what "brings" the other /speckit-*
commands into Cursor for THAT workspace.
Important:
- Open the sunfish-a16-rom folder in Cursor (not buildout-saar) so those
skills are available.
- /speckit-taskstoissues is GitHub-only — do NOT use it (we use Gitea later).
- We already wrote constitution + specs/001-003 manually. We do NOT need to
re-run /speckit-specify to create 001.
================================================================================
2) TYPICAL UPSTREAM ORDER (generic Spec Kit)
================================================================================
For a brand-new feature from scratch, the usual order is:
1. /speckit-constitution → project principles (once / rare)
2. /speckit-specify → create specs/NNN-name/spec.md from a prompt
3. /speckit-clarify → optional: ask structured questions
4. /speckit-plan → create plan.md (+ design artifacts)
5. /speckit-tasks → create tasks.md checklist
6. /speckit-checklist → optional quality checklist
7. /speckit-analyze → optional consistency report
8. /speckit-implement → execute tasks
9. /speckit-converge → later: find remaining work
You do NOT need every optional step every time.
================================================================================
3) ORDER FOR THIS PROJECT (sunfish-a16-rom) — RIGHT NOW
================================================================================
Already done:
[x] specify init (skills installed)
[x] custom constitution written (do NOT overwrite with generic /speckit-constitution
unless you intentionally re-seed — our constitution is already project-specific)
[x] specs 001, 002, 003 written
[x] feature.json → 001-development-environment
[x] design frozen at constitution 0.1.5
Recommended next commands (only for feature 001):
A. (optional) /speckit-clarify
Use if you want the agent to ask remaining ambiguities about 001 only.
Many ambiguities were already closed in chat — can skip.
B. /speckit-plan
Generates specs/001-development-environment/plan.md from the existing
spec.md + constitution. NO VM required.
C. /speckit-tasks
Generates tasks.md from the plan. NO VM required.
D. (optional) /speckit-analyze or /speckit-checklist
E. /speckit-implement
THIS is when rom-dev01 should exist (SSH working). Implementation of 001
assumes the VM is already provisioned outside this project.
Then later, switch feature and repeat plan→tasks→implement for:
002 → 003 → …
How to switch feature later (when 001 is done):
- edit .specify/feature.json:
{ "feature_directory": "specs/002-axp-reference-capture" }
- or use /speckit-specify only when creating a NEW feature number (004+)
- do NOT re-specify 001/002/003
================================================================================
4) WHAT EACH COMMAND DOES (short)
================================================================================
/speckit-constitution Seed/update constitution from template — SKIP for now
(ours is already custom 0.1.5)
/speckit-specify Create a NEW feature spec from a prompt
(we already have 001–003)
/speckit-clarify Ask clarifying questions before plan
/speckit-plan Write implementation plan (plan.md)
/speckit-tasks Break plan into tasks.md
/speckit-checklist Generate validation checklists
/speckit-analyze Cross-check spec/plan/tasks consistency
/speckit-implement Execute tasks against the real system/repo
/speckit-converge Find leftover work after partial implementation
/speckit-taskstoissues DO NOT USE (GitHub-specific)
================================================================================
5) PRE-PLAN BRIEFING FOR CHATGPT — FEATURE 001
================================================================================
Please review this briefing and return:
- anything missing/dangerous in the 001 plan approach
- suggested plan.md section outline
- P1 vs P2 sequencing recommendations
- explicit "do not do" list for Cursor when running /speckit-plan and later
/speckit-implement
--- PROJECT FACTS ---
Name: sunfish-a16-rom
Goal: LineageOS 23.2 / Android 16 custom ROM for Pixel 4a (sunfish), aiming at
AXP.OS Pro functionality with deliberate substitutions (Magisk→KernelSU,
backup→NeoBackup external, etc.)
Meta repo (this Spec Kit tree): eventually Gitea sunfish-a16-rom
Companion repos later: sunfish-rom-manifest, sunfish-rom-build, sunfish-rom-patches
Laptop role: Cursor Remote SSH, adb/fastboot, rsync, thin client
Build host: rom-dev01 (Ubuntu 24.04) — created OUTSIDE this project via
SSYSTEMS Salt/PVE by the human. Spec Kit must NOT create the VM.
001 starts ONLY when rom-dev01 already exists with:
- Ubuntu 24.04
- public IPv4, SSH works
- saltenv=base already applied by SSYSTEMS
Sizing to plan around (do not assume more):
6 vCPU / 52 GB RAM / 2 TB disk / 32 GB swap
→ conservative build parallelism later (003), not in 001 yet
001 IN SCOPE (ROM host environment):
- android-build user/workspace
- Android/Lineage build packages, git, git-lfs, repo, ccache
- /srv/rom layout (sources, artifacts, logs, tooling, reference)
- private Gitea (bind/tunnel; not world-exposed)
- four repos: sunfish-a16-rom, sunfish-rom-manifest, sunfish-rom-build,
sunfish-rom-patches
- off-VM backup/mirror of those Git repos (laptop/Borg/etc.)
- Ansible + Semaphore (P2; smoke playbook OK first)
- Spec Kit meta repo available on host
- Cursor Remote SSH + rsync artifacts workflow docs
- guest isolation posture docs/config (public NIC only; UIs not world-open)
[human already accepted keeping this in 001]
001 OUT OF SCOPE:
- Proxmox / pm-test01 / choosing public IP / company Salt VM creation
- Lineage sync/build (003)
- AXP capture / NeoBackup phone backup (002)
- microG/Phonesky/KernelSU/hardening
P1 vs P2 in 001:
P1: SSH + android-build + toolchain + /srv/rom + docs for thin-client workflow
(enough to unlock 003 later)
P2: Gitea + four repos + off-VM mirror + Ansible/Semaphore + isolation polish
Priority for ChatGPT review of the future plan.md:
- Must not invent PVE/qm/Salt SSYSTEMS steps
- Must not require Ansible on the laptop
- Must not put .repo/out/ccache on the laptop
- Must keep Gitea/Semaphore off public Internet by default
- Must include off-VM git backup
- Must use account android-build (not hard-code laptop username)
- First LOS build remains manual later (003); 001 only prepares the host
Authoritative files for Cursor when planning:
- .specify/memory/constitution.md (0.1.5)
- specs/001-development-environment/spec.md
- AGENTS.md
- docs/axp-pro-parity.md and docs/hardening-backlog.md are NON-NORMATIVE
Suggested plan.md outline for /speckit-plan to produce:
1. Summary / goals
2. Assumptions (VM pre-exists)
3. Non-goals
4. Host layout (/srv/rom, android-build home)
5. Package/toolchain set (LOS build deps)
6. ccache defaults for 6 vCPU / 52 GB
7. Gitea install + access model (SSH tunnel)
8. Initial empty repos + push meta Spec Kit tree
9. Off-VM backup/mirror procedure
10. Ansible + Semaphore minimal bootstrap
11. Isolation/firewall/DNS guest posture (docs + config)
12. Laptop workflow (Remote SSH, rsync, adb/fastboot)
13. Acceptance mapping to FR/SC in spec.md
14. Handoff criteria to start 002/003
Please critique this outline and list concrete plan bullets Cursor should include
or avoid.
================================================================================
6) AFTER CHATGPT ANSWERS
================================================================================
Human brings ChatGPT feedback into Cursor, then runs:
/speckit-plan
(optionally paste ChatGPT constraints as $ARGUMENTS)
Then:
/speckit-tasks
Implement only when rom-dev01 SSH works.
================================================================================
END
================================================================================
+31
View File
@@ -0,0 +1,31 @@
# sunfish-a16-rom — laptop OpenSSH (independent of SSYSTEMS Salt/GitLab)
# Included from: ~/.ssh/config.d/a15-rom-dev01
#
# Web UIs (no tunnel): https://a16.ssystems.de/ and /semaphore/
# HTTP Basic: /srv/rom/secrets/web-htpasswd.txt on host
# Default: root (ssh a16 / ssh a16.ssystems.de)
Host a16 a16.ssystems.de a16-root rom-dev01-root a15-root
HostName 49.13.104.83
User root
Port 37037
IdentityFile ~/.ssh/id_ed25519_rom_dev01
IdentitiesOnly yes
ForwardAgent no
# Build user when needed: ssh a16-build
Host a16-build rom-dev01 a15
HostName 49.13.104.83
User android-build
Port 37037
IdentityFile ~/.ssh/id_ed25519_rom_dev01
IdentitiesOnly yes
ForwardAgent no
Host a15.ssystems.de aventura.serv.ssystems.de
HostName 49.13.104.83
User root
Port 37037
IdentityFile ~/.ssh/id_ed25519_rom_dev01
IdentitiesOnly yes
ForwardAgent no
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff