repo-dev now exists on archr-linux/archr-repo with all 543 assets
(535 packages + 8 db artifacts) signed via the master subkey we
generated yesterday. SigLevel PackageOptional DatabaseRequired
matches the SteamOS/EndeavourOS/Arch-ARM model.
Open follow-ups noted in the entry:
- CI hook so the publish is no longer a manual one-hour job after
every build.
- Channel promotion (dev -> next -> stable) when v2.0 final lands.
- gen-pacman-repo version parser fails on 5 packages whose
package.mk uses a placeholder (gstreamer family, vitaquake2-lr
variants); not a runtime blocker but the dirnames are ugly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
First repo-dev release uploaded 1000/1078 assets before hitting
GitHub's documented per-release limit of 1000 assets. The cap is
hit because every package shipped a paired .sig, doubling the
count without adding meaningful security.
The trust path now flows entirely through the database:
- archr-keyring ships the master public key.
- archr.db.sig proves the db came from the maintainer.
- The db records the SHA256 of every package; pacman re-hashes
after download and refuses mismatches.
To compromise a package the attacker would have to publish a
modified db that lists their hash, which requires the signing
subkey. The per-package .sig was redundant against this attack.
This is the same model Arch Linux ARM, EndeavourOS and SteamOS
use in production.
pacman.conf SigLevel flips from "Required DatabaseOptional" to
"PackageOptional DatabaseRequired"; comment expanded so a future
reader sees the why.
gen-pacman-repo drops the `gpg --detach-sign` step inside
build_one_pkg(); repo-add --sign still signs the db at the end.
Asset count shrinks from 1078 to 543 (535 packages + 8 db
artifacts), which fits comfortably in a single GitHub Release.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The ROCKNIX-sourced rewrite of 003-midgard-refactor-power-init-and-
fixed-unbalanced-run.patch (picked up in 5805267f90) targets a
different kbase tree: it patches platform/devicetree/mali_kbase_runtime_pm.c
expecting `else if (!regulator_is_enabled(...))` on line 38.
Our mali_kbase pin (github.com/archr-linux/mali_kbase @ 422e192b)
does not have that line, so the hunk rejects and mali-bifrost:target
fails to install.
Restore the original 199-line patch that targets platform/meson/
mali_kbase_runtime_pm.c, which is the path that exists in our tree.
This was the file as of commit 5805267f90^ — the working pre-sync
state.
The ROCKNIX cleanup will become applicable when (and if) we move to
the same kbase revision they ship, which would itself be a bigger
upgrade.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
In F7 I added explicit bridge symlinks for hatari and openbor:
ln -sf /storage/.hatari /var/lib/archr/data/hatari
ln -sf /storage/openbor /var/lib/archr/games/openbor
That breaks the build at install time. The F5/F6 umbrella bridges
already turn /var/lib/archr/data and /var/lib/archr/games into
symlinks (-> /storage/.local/share and /storage/roms). When the
hatari ln -sf tries to create a child node under
/var/lib/archr/data, it follows the umbrella symlink, hits
/storage/.local/share which does not exist on the rootfs at build
time, and aborts with "No such file or directory".
Resolution: rely on the umbrella alone. /var/lib/archr/data/hatari
resolves to /storage/.local/share/hatari at runtime through the
umbrella; start_hatari.sh and start_OpenBOR.sh already mkdir -p the
final dir on first launch, so the dir materializes when needed.
Keep the /opt/jdk bridge because there is no /opt umbrella to fall
through.
This is the second clean-build fix on top of the wayland patch
rebase (7eff5d02a6).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The 00-fix-wayland-scanner-pkgconfig.patch was anchored on a context
line ('bindir=' + join_paths(...)) that Wayland removed between 1.24
and 1.25. After our F0 bump to 1.25.0 the unpack step rejected the
patch and aborted the build_compat arm pass.
Pull the rebased patch from ROCKNIX (commit a9e464274a in their
graphics/display sync) where the same context shift was already
resolved: the bindir line is gone, the filebase line stays.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three bugs caught while smoke-testing against an existing
build.ArchR-RK3326.aarch64/install_pkg tree:
1. BUILD_DIR auto-detection glob did not include the trailing arch
suffix, so build.ArchR-RK3326.aarch64 never matched. Now matches
build.*-RK3326*aarch64*.
2. Version sanitization used `echo | tr -c '[charset]' '.'` which
turns the trailing newline into a stray '.', producing versions
like "1.0.8." that pacman refuses. Swapped to printf.
3. Package layout was wrong for pacman 7 repo-add:
- .PKGINFO must be the FIRST entry in the tar (pacman streams it).
- No "./" prefix on file names (matches makepkg output).
- .PKGINFO needs `pkgbase` and `xdata = pkgtype=pkg` lines, not
just `pkgname`.
Rewrote the packing block to assemble an explicit file list with
.PKGINFO first, then tar -T.
Also: install_pkg name extraction is more robust now — sed strips
both 40-char git hashes and semver suffixes to recover the real
package name, and the package.mk lookup uses find rather than
literal globbing so packages nested at any depth are found.
Smoke test confirms repo-add accepts the resulting archive, signs
archr.db with the configured signer, and emits the expected
symlinks archr.db -> archr.db.tar.gz.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Master generated 2026-06-23 offline (ed25519 cert-only with ed25519
sign subkey expiring 2028-06-23). Private master lives on encrypted
cold storage; the signer subkey is on the build workstation only.
Revocation certificate kept offline in two physical locations.
Fingerprint: 0CB282379EBB394EF380AEB98A762D5706C602A1
archr.gpg is the binary public keyring that pacman-key --populate archr
walks; archr-trusted gives the master full ownertrust (level 4) so
packages signed by its subkey are accepted; archr-revoked stays empty
until a key is actually revoked.
archr-keyring/package.mk already auto-detects these files (instead of
the empty placeholders it shipped before) and includes them in the
image. With the keyring populated, the next gen-pacman-repo run with
SIGNER set will produce a properly signed archr.db and per-package
.sig files that ArchR clients can verify.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Decision: pacman repo lives on GitHub Releases at
archr-linux/archr-repo. No VPS, no repo.arch-r.io. Google Drive stays
out of runtime (backup/archive only). Cloudflare R2 fallback deferred.
Detail in docs/release-policy.md.
archr-update rewritten end-to-end:
- 188 lines of dead POST-to-update.arch-r.io code replaced by an
87-line wrapper around pacman -Syu. The POST endpoint never existed
so there is no legacy compat to preserve.
- subcommands: check, update, info. system.cfg "updates.branch" maps
to repo-stable / repo-next / repo-dev tags on archr-linux/archr-repo;
the mirrorlist is rewritten on every invocation so channel switches
are immediate.
pacman.conf rebuilt around a single [archr] repo (Arch Linux ARM
upstream removed). SigLevel starts at "Required DatabaseOptional"
during bootstrap; will tighten to "Required" once the production
master key signs the .db.
pacman-init reachability test points at github.com and the keyring
populate target is "archr" instead of "archlinuxarm".
archr-keyring repurposed: the three archlinuxarm keyring files are
removed; package.mk now expects archr.gpg / archr-trusted /
archr-revoked under keys/ and ships empty placeholders until the real
ArchR master key is generated.
scripts/repo/gen-pacman-repo + README: take the build.*-RK3326 tree
that "make docker-RK3326" leaves behind, package each install_pkg/<x>
as a .pkg.tar.zst with synthesized .PKGINFO, run repo-add to build
archr.db, optionally GPG-sign everything. Emits a `gh release create`
command for the publish step. CI hook left as a follow-up.
docs/improvements.md 2.1 marked partial: client and build script are
ready; what blocks the first usable release is generating the GPG
master key, creating archr-linux/archr-repo on GitHub, and one manual
publish for the repo-dev tag to validate the end-to-end loop.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Quirks pruning done in 7644ce26d3 (352 files, 8037 lines). Splash
rename and runemu.sh refactor reclassified as organic follow-ups:
- splash sed stays until upstream archr-splash repo finishes the
internal rename.
- runemu.sh is 578 lines (not 1500+ as the original entry guessed);
refactor still pays in maintainability but is not a blocker.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
projects/ArchR/packages/hardware/quirks/ shipped 50 device dirs and
10 platform dirs inherited from JELOS/ROCKNIX. ArchR only ever
supports RK3326 (one platform), and the per-device quirks are
selected at runtime by reading /sys/firmware/devicetree/base/archr,device_switch/this
— a name that can only match a DTB we compiled in. The 42 device
dirs without a matching DTB and the 9 platform dirs for SoCs we
don't have (H700, RK3399, RK3566, RK3588, S922X, SDM845, SM8250,
SM8550, SM8650) were unreachable code.
Keep: 14 device dirs and 1 platform (RK3326). All correspond to a
DTS file in projects/ArchR/devices/RK3326/linux/dts/rockchip/ so
they can actually be selected at boot.
Cross-references to the dead platforms in other scripts (case
${DEVICE} in RK3399) ...; case ${DEVICE} in SM8250) ...) stay
as harmless dead branches that never trigger when DEVICE=RK3326;
cleaning those up is in scope for a follow-up sweep.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Section 2.2 now reflects what shipped: auditoria + 22 bridges + 7 vars
+ man page + 7 phases all marked done. Open item left explicit: the
1625 hardcoded /storage refs in legacy scripts continue to work via
compat and refactor organically.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three concrete Class Z migrations land via bridges in the archr meta-
package plus single-line script tweaks:
- /storage/jdk -> /opt/jdk (runemu.sh JAVA_HOME, freej2me.sh JDKDEST)
- /storage/.hatari -> /var/lib/archr/data/hatari (start_hatari.sh
HATARI_DIR_HOME; the pre-baked Atari-ST .cfg files keep working
through the symlink because both addresses resolve to the same
overlay dir).
- /storage/openbor -> /var/lib/archr/games/openbor (start_OpenBOR.sh
CONFIGDIR).
Other Class Z paths fall out cleanly:
- /storage/.opt was already covered by /opt -> /storage/.opt symlink
in virtual/image/package.mk.
- /storage/.emulationstation already gets redirected to
/storage/.config/emulationstation by emulationstation's autostart,
which is in turn covered by the umbrella bridge F5.
- /storage/psvita/vita3k/ux0/app and /storage/.nfs-mount are
low-traffic top-level paths; no bridge added.
docs/fhs-mapping.md: Fase 7 detail + system-wide audit summary. 17
umbrella bridges in archr meta-package + 5 modular (openssh, iwd,
bluez, connman, fontconfig). All seven ARCHR_* variables resolve to
Arch-friendly paths. The remaining 1625 hardcoded /storage refs in
projects/ArchR continue to work through compat (both paths reach the
same storage overlay); refactoring those is left as an organic
follow-up.
Status header marks the FHS migration complete. Section 2.2 of the
Arch-ification roadmap is now closed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Same playbook as F5, applied to user data. /storage/roms appears in
117 files in projects/ArchR and refactoring every reference in one
shot would be a huge diff over the directory where the user's games
library lives. Bad risk-reward.
Two new umbrella bridges in the archr meta-package:
/var/lib/archr/games -> /storage/roms
/var/lib/archr/backup -> /storage/backup
ARCHR_GAMES flipped to the FHS path; man page mirrors it.
The other Class E paths (screenshots, recordings, downloads, music,
pictures, videos, tvshows) audit as zero references in
projects/ArchR; they are LibreELEC top-level heritage and not used at
ArchR runtime.
/storage/.ssh stays as is. busybox/package.mk sets the root user's
HOME to /storage, so /storage/.ssh is literally $HOME/.ssh — FHS
already. Moving to /root/.ssh would require flipping the HOME of root
to /root first, which has knock-on effects (cd ~, every script that
assumes cwd at login, etc) outside the FHS scope.
No data migration script: nothing on disk moves. ROMs and saves keep
exactly the same bytes; the FHS path is just another way to address
them.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Original F5 plan was "one commit per emulator, refactor every
hardcoded /storage/.config/<emu> reference". Audit showed that path
appears across 22+ device quirk files, multiple start scripts and a
handful of compiled-in patches per emulator. Mass refactor would be a
huge diff with real regression risk and zero FHS gain on the running
system because, with the bridge installed, the FHS path already
resolves correctly.
Pivot: install two umbrella bridges in the archr meta-package:
/var/lib/archr/config -> /storage/.config
/var/lib/archr/data -> /storage/.local/share
Flip the ARCHR_CONFIG and ARCHR_DATA exports in 010-archr-fhs to the
FHS paths so downstream scripts that already use the variables get
the Arch-friendly surface for free. Update archr(7) ENVIRONMENT
section: both variables now describe the FHS location as the actual
address, with /storage/... noted as the rw-overlay substrate.
Net effect: every /var/lib/archr/config/<emu> and
/var/lib/archr/data/<emu> path exists at runtime and is writable.
Inspection tools see a normal Arch layout. Hardcoded /storage/.config
references in JELOS-inherited quirks and start scripts keep working
because the symlink resolves both ways — writes through either path
land in the same overlay file. Per-emulator refactors stay possible
later, as incremental PRs, without a single big-bang.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fase 4 in fhs-mapping.md gets the breakdown: 9 state dirs bridged in
the four F4.x commits (ssh, iwd, bluez, services, samba, connman,
wireguard, tailscale, cron), pacman db/cache and rfkill noted as
no-op (already bridged upstream), .local/share moved to F5 (it is
per-emulator data and stays with each emulator's config migration),
overlayfs upperdirs documented as permanent no-op for the same
constraint that pinned the F3 workdirs, .pacman/build deferred to
section 2.1 when pacman gets wired into userland.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wider Class D batch. Every state directory referenced by a systemd
unit or runtime script now lives at an Arch-conventional path with a
bridge symlink into /storage:
service enable/disable flags
/storage/.cache/services -> /var/lib/archr/services
ConsumerS: 099-networkservices, archr-report-stats.timer, and the
12 unit files that gate themselves on .conf / .disabled sentinels
(avahi-daemon, avahi-defaults, sshd, nmbd, smbd, simple-http-server,
syncthing, tailscaled, zerotier-one, cron, cron-defaults). The
systemd-scripts/usercache-setup migration helper updated too.
samba passdb
/storage/.cache/samba -> /var/lib/samba/private (Arch standard).
smb.conf passdb line + 007-rootpw boot init script updated.
connman + wireguard
/storage/.cache/connman -> /var/lib/connman
/storage/.config/wireguard -> /etc/wireguard (vpn_storagedir)
Bridges installed by the connman package; connman-setup script
follows the bridge.
tailscale
/storage/.cache/tailscale -> /var/lib/tailscale.
tailscaled --state= flag flipped to the FHS path; bridge in archr
meta-package.
cron
/storage/.cache/cron -> /var/spool/cron (Arch convention; busybox
crond reads spool/<user>). cron.service ExecStartPre updated.
archr meta-package now ships seven Class D bridge symlinks alongside
the five Class C bridges from earlier phases. Everything else stays
on the storage overlay; nothing on disk moved.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
bluez configure storagedir flipped to /var/lib/bluetooth (Arch
convention for paired-device state). post_makeinstall_target plants
the bridge symlink to /storage/.cache/bluetooth so pairings persist
on the rw overlay across reboots.
archr-config forgetBT helper and backuptool default list also point
at the FHS path.
tmpfiles z_05_bluez.conf intentionally keeps creating
/storage/.cache/bluetooth as the real overlay dir.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two security-sensitive state dirs migrated to Arch-standard locations.
openssh:
- --with-keydir flipped from /storage/.cache/ssh to /var/lib/sshd.
- post_install plants a bridge symlink /var/lib/sshd ->
/storage/.cache/ssh so keys persist on the rw overlay.
- sshd.service updates all three ExecStartPre paths.
- tmpfiles z_04_openssh.conf keeps creating /storage/.cache/ssh as
the real overlay dir.
iwd:
- Environment STATE_DIRECTORY in iwd.service now /var/lib/iwd.
- post_makeinstall_target adds the /var/lib/iwd ->
/storage/.cache/iwd bridge symlink.
- wifictl (IWD_DIR, IWD_AP_CFG_DIR), post-update copy target and
userconfig-setup all flipped to /var/lib/iwd.
- tmpfiles z_03_iwd.conf keeps creating the backing dirs.
Both follow the same bridge pattern as F2/F3: FHS surface, storage
overlay substrate. SSH host keys and wifi credentials survive the
migration because the underlying bytes never move (symlink swap).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Class F (logs) and Class G (temp/update) reshuffled.
Logs:
- /var/log was already a mount from /storage/.cache/log via
packages/sysutils/busybox/system.d/var-log.mount, so journald already
writes to /var/log/journal in FHS terms. runemu.sh debug log moves
from /storage/.cache/log/runemu-debug.log to /var/log/runemu-debug.log
(same physical bytes, FHS surface).
- createlog (top-level busybox script) packs archive zips into
/var/log/archr/archive instead of /storage/logfiles. ArchR tmpfiles
z_01_archr.conf creates the backing dir under the storage overlay so
the path is writable on first boot.
Update:
- /var/cache/archr/update is a bridge symlink to /storage/.update added
by the archr meta-package. archr-update, automount and the [update]
samba share all flipped to the FHS path.
- factoryreset still preserves /storage/.update by its real name in
the find regex; that path is also what the pre-boot bootloader
scripts read, so the backing path is the canonical one and the
bridge is just the userland surface. Comment added so this is
obvious to future readers.
- busybox/scripts/init keeps UPDATE_ROOT=/storage/.update because it
runs in the initramfs before /var is even available.
Overlayfs workdirs (/storage/.tmp/*-workdir) and /storage/.boot.hint
documented as no-op in fhs-mapping.md: the overlayfs kernel layer
requires upperdir and workdir to share a filesystem, and the boot
hint is part of the U-Boot handshake.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Last Class C bridge: /var/cache/archr/fstrim.run -> /storage/.cache.
The stamp file does not exist at first boot; touch follows the symlink
and creates the file on the storage side, where future fstrim runs
read it to remember the last run.
docs/fhs-mapping.md updated:
- Fase 2 marked complete with the 5 caches actually migrated
(mesa, fontconfig, kernel-overlays, locpath, fstrim.run).
- Documents that the initial Class C bucket of 44 paths was a coarse
auto-classification: most of those paths are state (-> F4 / Class D),
logs (-> F3 / Class F) or already redirected upstream.
- Three already-no-op redirects called out: journald.conf.d,
systemd-machine-id, system_timezone (already symlinked by their
owning packages).
Next: F3 (Class F+G, logs and temp).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two more Class C bridges, same pattern as F2.1/F2.2:
- /var/cache/archr/kernel-overlays -> /storage/.cache/kernel-overlays
- /var/cache/archr/locpath -> /storage/.cache/locpath
Consumers flipped to the Arch-friendly path:
- busybox/scripts/kernel-overlays-setup: OVERLAY_CONFIG_DIR
- busybox/profile.d/10-locale.conf: LOCPATH
- wayland/util/foot/scripts/foot.sh: LOCPATH (ArchR override only)
Service files (locale.service, kernel-overlays tmpfiles.d) keep
addressing the /storage backing path because they are the layer that
creates the actual rw overlay dir; the bridge symlink points at it.
Top-level packages/wayland/util/foot/scripts/foot.sh is upstream
LibreELEC heritage and stays untouched; the ArchR override is the
file the runtime actually executes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fontconfig's cache directory is baked into the binary at configure
time. Flip --with-cache-dir from /storage/.cache/fontconfig to the
Arch-standard /var/cache/fontconfig and install a bridge symlink
pointing back to the storage overlay so the cache survives reboots.
Same bridge pattern as the systemd /etc redirects and the F2.1 mesa
shader cache: surface looks FHS, substrate stays on /storage.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
First migration of the Class C cache work: the Mesa shader cache moves
from the LibreELEC-style /storage/.cache/mesa_shader_cache to the
Arch-standard /var/cache/mesa.
Implementation follows the bridge-symlink pattern systemd already uses
for /etc/*: the archr meta-package creates /var/cache/mesa as a
build-time symlink into /storage/.cache/mesa_shader_cache. Mesa,
runemu.sh and any inspector see the FHS path; writes still land on the
storage overlay so the cache survives reboots.
archr/package.mk: mkdir /var/cache, ln -sf the symlink.
quirks/profile.d/041-panfrost: MESA_SHADER_CACHE_DIR=/var/cache/mesa.
Comment explains the bridge so future readers do not assume tmpfs.
runemu.sh: create the cache dir through /var/cache/mesa instead of
the legacy /storage path. Comment updated to mention the symlink.
systemd/tmpfiles.d/z_01_archr.conf is intentionally left targeting the
backing path /storage/.cache/mesa_shader_cache: it is internal plumbing
that must guarantee the real overlay dir exists; /var/cache/mesa is
the surface, /storage/.cache/mesa_shader_cache stays the substrate.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Audit of projects/ArchR/packages/sysutils/systemd/package.mk showed
the Class A paths are already FHS-correct at runtime: systemd creates
build-time symlinks from /etc/modules-load.d, /etc/sysctl.d,
/etc/tmpfiles.d, /etc/udev/{hwdb,rules}.d and /etc/systemd/*conf.d
pointing into /storage/.config/.
For any tool inspecting the running system, /etc/modules-load.d is a
real populated directory in /etc. The fact that the symlink target
lands on the storage overlay is internal plumbing required because
the rootfs is squashfs read-only.
Repointing those targets to /var/lib/archr/etc would be cosmetic in
exchange for adding a bind-mount in the init script. Not worth it.
hosts/resolv keep their existing template+render pipeline
(network-base-setup reads /storage/.config/{hosts,resolv}.conf and
writes /run/archr/{hosts,resolv}.conf at boot).
Fase 1 closed without code change. Fase 2 (Class C: cache) is next
and has actual work.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
profile.d/010-archr-fhs exports seven variables (ARCHR_ETC,
ARCHR_CONFIG, ARCHR_CACHE, ARCHR_DATA, ARCHR_GAMES, ARCHR_LOG,
ARCHR_TMP) that future ArchR scripts should read instead of
hardcoding /storage/.config and siblings. Every variable defaults to
its current /storage location so behaviour is identical to today;
subsequent FHS phases will flip the right-hand side without touching
the callers.
archr(7) gains an ENVIRONMENT section describing each variable, its
current value and its FHS target so anyone writing a new script knows
which name to use.
docs/fhs-mapping.md: F0 marked complete; F1..F7 still pending.
Risk: zero. The new file is read at login but only sets variables.
No script reads them yet.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>