Commit Graph

247 Commits (29f6c62008d3e4685ab0b94713f426adfea70780)

Author SHA1 Message Date
Amir Alexander Abdelbaki 29f6c62008 feat(greeter): switch to greetd+ReGreet (Wayland), fix FIDO/scaling/wallpaper
Replace the Xorg lightdm-gtk-greeter with a wlroots-native greetd + ReGreet
greeter (inside a cage kiosk), fixing three issues rooted in running an X
greeter on a multi-monitor / HiDPI / Wayland box: a blank output, tiny
scaling, and the Xorg->Wayland VT/seat handover race that left the Hyprland
session with dead keyboard/mouse.

- etc-greetd/: greetd config, ReGreet config (cyberqueer GTK4 theme, larger
  font, wallpaper background), cage session launcher, and a password + FIDO
  PAM stack (no securetty/OATH, which a graphical greeter cannot drive).
- greetd-regreet.sh: install/enable module, mutually exclusive with the
  lightdm and ly greeter modules.
- Greeter background tracks the desktop wallpaper: a root systemd .path unit
  mirrors the picker's choice to a greeter-readable path (the user's home is
  0700, so the unprivileged greeter cannot read it directly).

The LightDM path is kept as a dormant alternative and hardened en route:
pam_u2f (FIDO) added to its PAM stack, a HiDPI + multi-monitor
display-setup.sh, a larger greeter font, and the same wallpaper-sync units.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 03:16:12 +02:00
Amir Alexander Abdelbaki d1e5f765aa feat(greeter): switch HyprLua to LightDM, fix Plymouth splash, extract ly module
Plymouth: bg-skull.svg is a 1920x1080 transparent composition but was rendered
at 300px and treated as a small centred logo, so the skull all but vanished and
the spinner (positioned off the logo height) landed on the monitor artwork. Now
render at native 1920px, scale to fill the screen, and pin the spinner at a
fixed 0.93 of screen height. Also fix DOTFILES_DIR (was off by one level, so the
bundled SVG never resolved and it fell back to a transparent placeholder) and
the dead ../plymouth.sh paths in plymouth-custom.sh and install-modules.sh.

Greeter: HyprLua now uses LightDM + lightdm-gtk-greeter, themed with the
existing cyberqueer GTK theme and a rendered skull background, via a new
self-contained lightdm module. ly is extracted from the DE scripts into its own
module so it stays installable; niri and legacy hyprland keep their inline ly.
Both greeters are registered in modules.conf as mutually exclusive, and the
generated installer/answerfile/docs regions were rebuilt from it. install-modules.sh
guards the 'ly' token with a padded match so it can't substring-hit 'p(ly)mouth'.

Add setup/tools/migrate-to-lightdm.sh to move an existing install (ly / greetd /
GDM / SDDM / LXDM) onto the Plymouth + LightDM setup: it disables the old DM for
the next boot only (never stops the running session) and delegates install +
theming to the module scripts. Supports --dry-run / --no-plymouth / --yes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 01:53:37 +02:00
Amir Alexander Abdelbaki 2ffedd076e fix(installer): flush stray tty input, split shell rc's from shell setup
The first checklist (Select Components) could silently auto-confirm its
defaults, swallowing the user's toggle: tui-install.sh runs right after the
long, mostly-unattended base install, and keystrokes buffered during that
wait were consumed by the script's very first read. Drain the tty before the
first prompt.

Also split the "shell" component into "shell" (tools/plugins/programs) and
a new "shellrc" (personal .bashrc/.zshrc/.vimrc), so the shell environment
can be installed without overwriting the user's rc files. shell-setup.sh
reads MARCHY_DEPLOY_SHELL_RC to gate the rc symlinks and now also deploys
.vimrc, which /etc/skel sync already expected but nothing ever created.
Removes the separate, easily-desynced "Shell Config for New Users" prompt
in favor of deriving /etc/skel behavior from the same shellrc choice, and
mirrors the removal in generate-answerfile.sh.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 14:22:03 +02:00
Amir Alexander Abdelbaki 4f22a3b03f refactor(ansipa): dev_security-scan → dev_mon_clamscan, report to CheckMK; drop SMB scan pipeline
Rename the security-scan policy to dev_mon_clamscan (fits the XXX_mon_* naming
for monitoring groups) and make it hand results to CheckMK natively instead of
uploading logs to an SMB share.

- dev_mon_clamscan.sh: runs the daily ClamAV/rkhunter/chkrootkit scan and writes
  its own CheckMK local check (ansipa_clamscan → service "Ansipa_ClamScan"); the
  agent from dev_mon_base collects it. Removes the SMB upload entirely. Discovery
  matches dev_mon_clamscan BEFORE the generic dev_mon_* catch-all so it sets
  WANT_CLAMSCAN rather than being treated as a dev_mon.sh check. mkdir -p the
  cron dir (minimal hosts lack /etc/cron.d until cron is installed).
- Remove the now-redundant dev_mon_malware check from dev_mon.sh (subsumed).
- Remove usr_scan-notify and the whole SMB scan-alert pipeline it fed:
  ansipa-fetch-alerts.sh, ansipa-scan-notify.sh, server ansipa-check-scans.sh,
  the ansipa-scans Samba share + scanupload account, /etc/ansipa-smb.creds, and
  the SMB_SCAN_PASSWORD/smb_scan_password variables (playbook, compose, run.sh,
  .env, Dockerfile). The ansipa-luks-keys and ansipa-policystore shares stay.

Verified live on the Arch client: enrolling in dev_mon_clamscan writes the scan
script, cron, and local check (which reports "Ansipa_ClamScan"); leaving the
group removes all three. Docs updated (policy tables, architecture, SMB shares,
scan-flow → CheckMK).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUhrcFU8J1Hnf7vNqNxZNi
2026-07-02 13:49:18 +02:00
Amir Alexander Abdelbaki adea0f457d fix(ansipa): reliable nginx + keycloak healthchecks
Both containers were reporting "unhealthy" while serving fine — the
healthchecks I added probed the wrong address/endpoint:

- nginx: `wget localhost` resolves to IPv6 ::1 inside the container, but nginx
  listens on IPv4 0.0.0.0:80 only, so the probe was refused. Use 127.0.0.1.
- keycloak: with KC_HTTP_RELATIVE_PATH=/auth, /health/ready on the 9000
  management port returns 404. Probe /auth/realms/master on the serving port
  8080 instead (bash /dev/tcp — the image has no curl/wget).

Verified all five containers report healthy and the gateway serves /, /ipa/ui/,
/cmk/ and /auth/ correctly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUhrcFU8J1Hnf7vNqNxZNi
2026-07-02 13:22:24 +02:00
Amir Alexander Abdelbaki dac9060516 fix(de): niri — disable greetd so ly is the sole greeter
niri contradictorily enabled BOTH ly@tty1 and greetd.service (it deployed a
greetd-tuigreet config and enabled greetd late in the script) while also
configuring and enabling ly — so two display managers raced for tty1 and the
stale greetd/tuigreet greeter could win. niri's intent is ly (it disables
getty@tty1, deploys ly config, enables ly@tty1).

Add disable_service greetd.service next to the ly enable and remove the
greetd-config-deploy-and-enable block, matching the hyprlua/hyprland fix. ly
lists the niri Wayland session from /usr/share/wayland-sessions, so no greetd
path is needed. Verified the same fix live on a hyprlua install (greetd
disabled, ly@tty1 enabled).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUhrcFU8J1Hnf7vNqNxZNi
2026-07-02 13:15:12 +02:00
Amir Alexander Abdelbaki 5543194bec fix(modules): correct wrong package names/sources found by static audit
Statically audited every optional-module and DE script's package tokens
against the official repos, the AUR, and Flathub. Ten modules named a package
that does not exist at the source they used, so each would abort with "target
not found" (pacman), an AUR miss, or a Flathub 404:

Installed via pacman but AUR-only (switched to the yay AUR helper):
  - tetris:   bastet
  - doom:     chocolate-doom, freedoom
  - openarena:openarena
  - openshot: openshot
  - freeipa-client (module): oddjob — now optional via yay with a
    pam_mkhomedir fallback (matches freeipa-enroll.sh)

Wrong AUR/repo package names:
  - cecilia:      cecilia -> cecilia-git
  - productivity: python-jrnl -> jrnl (in the official repos)
  - localtunnel:  AUR fallback localtunnel -> nodejs-localtunnel

Wrong/absent Flatpak IDs:
  - zen-browser: io.github.zen_browser.zen -> app.zen_browser.zen
  - stuntrally:  io.github.stuntrally.StuntRally3 is not on Flathub at all;
    switch to the AUR stuntrally-bin (the actual Stunt Rally 3)

Verified all corrected names resolve (repo / AUR RPC / Flathub API). Modules
whose tokens are in the official repos and merely installed via yay (nyxt,
toot, llama-cpp — yay handles both sources) were left as-is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUhrcFU8J1Hnf7vNqNxZNi
2026-07-02 12:55:59 +02:00
Amir Alexander Abdelbaki be757fda5a feat(ansipa): nginx gateway — reverse proxy + portal start-page
Add an nginx service that fronts all three web UIs on a single hostname/port
and serves a portal landing page, designed to sit behind the operator's own
TLS-terminating reverse proxy.

- Each backend is proxied at the path it already serves natively — FreeIPA
  /ipa, CheckMK /cmk, Keycloak under a configured /auth relative path — so no
  fragile path rewriting is needed and one upstream proxy line exposes
  everything. FreeIPA's strict Host/Referer anti-CSRF checks are satisfied by
  pinning Host to the IPA hostname and rewriting Referer.
- Honors incoming X-Forwarded-Proto/For so it works behind a second reverse
  proxy (double-proxy verified: portal, FreeIPA, CheckMK and Keycloak all
  reachable through two layers, and a CheckMK login POST completes to an
  authenticated session through the gateway).
- Keycloak: KC_HTTP_RELATIVE_PATH=/auth + KC_PROXY_HEADERS=xforwarded so it
  builds correct URLs behind the gateway; deliberately not forwarding
  X-Forwarded-Port (this gateway is :80 internally; the public port is the
  upstream proxy's) to avoid https://host:80 redirects.
- .env.example documents ANSIPA_HTTP_PORT and gives the exact upstream
  reverse-proxy snippet (nginx location block + Caddy one-liner).

Portal is a static cyberqueer-themed page linking to /ipa/ui/, /cmk/, /auth/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUhrcFU8J1Hnf7vNqNxZNi
2026-07-02 12:36:28 +02:00
Amir Alexander Abdelbaki b21f415cde fix(ansipa): hostname-binary-free FQDN + push-mode orchestration (NAT path)
Found while running the policy matrix and NAT/WAN scenario in VMs:

- ansipa-enforce-policies.sh and the dev_security-scan generated script called
  `hostname -f`, but the hostname binary (inetutils) is not installed on a
  minimal Arch system, so the enforcer aborted with "hostname: command not
  found" on every run. Add an ansipa_fqdn() helper (hostnamectl / getent /
  /etc/hostname / kernel-hostname fallback chain) and use it; inline the same
  fallback in the standalone scan script.

- Fix two stacked bugs that meant CheckMK push mode — the only monitoring
  transport that works when a client is behind NAT — never functioned:
  * ansipa-checkmk-setup.sh referenced IPA_ADMIN_PASS in the DONE_FLAG
    early-exit path, but that variable was assigned only afterwards, so every
    rerun kinit'd with an empty password, failed silently, and skipped the
    push-mode request writer. Resolve config before the early exit.
  * _write_push_requests parsed `ipa hostgroup-show dev_mon_base` members with
    awk that did `next` on the "Member hosts:" line — but ipa prints members on
    that same line, so the only line with hostnames was discarded and no push
    request was ever written. Rewrite the awk to capture the label line's value
    plus wrapped continuation lines. Verified end-to-end: requests are written
    and CheckMK flips the host to cmk_agent_connection=push-agent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUhrcFU8J1Hnf7vNqNxZNi
2026-07-02 12:25:03 +02:00
Amir Alexander Abdelbaki 0cd352da00 fix(de): disable greetd so ly is the sole greeter on hyprlua/hyprland
core.sh enables greetd (tuigreet) on every install, but the ly-based Hyprland
DEs only disabled getty@tty1 and enabled ly@tty1 — leaving both display
managers enabled and racing for tty1, so the stale greetd/tuigreet greeter
could win and show the old text login instead of ly.

Add a disable_service helper (mirrors enable_service; best-effort, non-fatal)
and call disable_service greetd.service in hyprlua.sh and hyprland.sh right
before enabling ly@tty1, so ly owns the login screen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUhrcFU8J1Hnf7vNqNxZNi
2026-07-02 11:28:15 +02:00
Amir Alexander Abdelbaki 0f767fb051 fix(ansipa): docker stack volume/network naming, healthchecks, client-routable CMK URL
Found while bringing up the FreeIPA+CheckMK+Keycloak stack in a VM:

- Pin volume and network names (name:) so compose and the cgroup-v2 run.sh
  wrapper share the same objects. Compose otherwise prefixes the project name
  (image_cmk-creds, image_ipa-net), so the run.sh-started freeipa container
  mounted a different cmk-creds volume than the compose-started checkmk wrote
  to — the automation secret never reached IPA and the dev_mon_* integration
  retried forever. run.sh also now labels the network it creates so compose
  adopts it instead of erroring "exists but not created by compose".
- run.sh: mount cmk-creds into freeipa and include checkmk in the "all" target;
  both were missing, so the CheckMK path was never wired up via run.sh.
- Fix both healthchecks: Keycloak has no curl/wget and serves /health on the
  management port 9000 only when KC_HEALTH_ENABLED=true, so the old curl probe
  left it permanently unhealthy — use a bash /dev/tcp probe against 9000.
  CheckMK's /api/1.0/version needs auth (401), so probe the login page instead.
- Advertise a client-routable CheckMK URL (CMK_ADVERTISED_URL) in the
  dev_mon_base description instead of the container-internal 172.30.0.12, which
  enrolled clients cannot reach; falls back to the internal URL when unset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUhrcFU8J1Hnf7vNqNxZNi
2026-07-02 11:28:00 +02:00
Amir Alexander Abdelbaki 3ee375b1c1 fix(ansipa): FreeIPA enrollment for Arch/Fedora/RHEL + autofs AUR workaround
Found while testing client enrollment across distros in VMs:

- Restore the executable bit on freeipa-enroll.sh (lost on a prior edit), which
  made freeipa-client.sh fail with a misleading "not found" for every path.
- Arch: freeipa-client is AUR-only, not the repo package "freeipa" — pacman -S
  freeipa died with "target not found". Install sssd/krb5 from repos and
  freeipa-client via an aur_install helper that drops to the sudo user.
- Bundle autofs-pkgbuild/: freeipa-client (AUR) hard-depends on autofs (AUR),
  whose PKGBUILD pins autofs-5.1.9.tar.xz — pruned from kernel.org at the 5.2.0
  release, so it 404s and blocks all Arch enrollment. Build autofs ourselves
  from upstream git pinned to a 5.1.9-series commit that already carries the
  cyrus-sasl function-pointer fix GCC 14+ needs, and install it before the
  freeipa-client AUR build so the broken AUR autofs is never touched. Verified
  the bundled PKGBUILD builds and installs cleanly on Arch.
- Fedora vs RHEL/Rocky/Alma use different client package names: keep
  freeipa-client on Fedora, use ipa-client on the RHEL family (freeipa-client
  does not exist there). Split the case arms accordingly.
- Guard both freeipa-client.sh entry points against unattended/no-TTY runs: the
  module TUI would hang on a dialog menu, and the wrapper would block on an
  interactive password read when the shipped answerfile has a blank password.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUhrcFU8J1Hnf7vNqNxZNi
2026-07-02 11:27:46 +02:00
Amir Alexander Abdelbaki 03db4f00a7 fix(ansipa): Arch package/hostname fixes, safer seed_policystore delegation
- Use samba/openssh package names on Arch instead of Debian-style samba-client/openssh-server
- Skip AUR-only freeipa install when ipa-client-install already present; skip oddjob if not in official repos
- Guard seed_policystore delegate_to when ipa_server_host is undefined
- Make hostname detection and OS version parsing more robust
- Fix inverted DNS_UPDATE/CONFIGURE_SUDO enrollment flags
2026-07-02 07:01:46 +02:00
Amir Alexander Abdelbaki 815c0e57a8 refactor(ansipa): split enforce-policies monolith into modular policies.d/ system
The 1550-line ansipa-enforce-policies.sh is replaced by a slim orchestrator
that sources policy files from policies.d/ — one file per policy type — via
a shared lib/ansipa-policy.sh that consolidates all IPA group discovery into
two queries (one host-show, one group-find) instead of five or more.

New files:
- ansible/lib/ansipa-policy.sh          shared constants, log/warn, _ansipa_discover(),
                                         _smb_parse_cred(), _ansipa_sync_policystore()
- ansible/policies.d/dev_daemon.sh
- ansible/policies.d/dev_timeshift-backup.sh
- ansible/policies.d/dev_security-scan.sh
- ansible/policies.d/dev_no-local-users.sh
- ansible/policies.d/dev_local-sudo.sh
- ansible/policies.d/dev_ssh.sh
- ansible/policies.d/dev_mon.sh         includes usr_mon_logins (same CMK dep)
- ansible/policies.d/usr_admin.sh
- ansible/policies.d/usr_block-binary.sh
- ansible/policies.d/usr_prt.sh
- ansible/policies.d/usr_scan-notify.sh
- ansible/policies.d/usr_smb.sh
- ansible/policies.d/usr_policystore.sh NEW: auto-mount ~/policystore for
                                         usr_smb_adm_policystore members

The policies.d/ folder on the FreeIPA container is exposed as an SMB share
(ansipa-policystore) so admins can live-edit policy files without Ansible.
The enforcer syncs from the share before each run; cached local files are
used as fallback when SMB is unreachable.

- ansipa-smb-setup.sh: adds ansipa-policystore share + smb-policystore system
  user + usr_smb_adm_policystore IPA group with auto-generated credential
- deploy-ansipa-policies.yml: deploys lib/ and policies.d/ to enrolled clients;
  adds optional seed_policystore task for bootstrap

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 19:27:35 +02:00
Amir Alexander Abdelbaki 0dd7ce3b51 docs(ansipa): document all policies; fix package check for pacman/dpkg
- ansipa-enforce-policies.sh header: add missing dev_ssh_*, usr_smb_r_*,
  usr_smb_rw_*, dev_mon_* (base/malware/timeshift/power), usr_mon_logins
  with concise descriptions matching actual behaviour.  Notes section
  updated with CheckMK setup prerequisite.
- ansipa_packages local check: replace broken rpm||dpkg fallback (wc -l
  always exits 0 so dpkg/pacman branches were never reached) with an
  if/elif/elif that probes for rpm, dpkg, or pacman in order.  Includes
  pacman.log-based recent-packages list for Arch hosts.
- deploy-ansipa-policies.yml prerequisites: add CheckMK and SMB mount notes.
- .env.example: add CheckMK section (CMK_ADMIN_PASSWORD, CMK_SITE_ID).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 18:46:27 +02:00
Amir Alexander Abdelbaki 6bb19cea78 feat(ansipa): integrate CheckMK CE monitoring
Adds full CheckMK CE 2.3 monitoring integration to the ansipa policy
enforcement system. Monitoring is group-driven — adding a host to an
IPA hostgroup installs the relevant agent checks automatically.

New monitoring groups:
  dev_mon_base      — install check-mk-agent, register host in CMK;
                      packages check + built-in CPU/RAM/disk/uptime
  dev_mon_malware   — ClamAV scan results from dev_security-scan log
  dev_mon_timeshift — Timeshift snapshot age (WARN >5d, CRIT >10d)
  dev_mon_power     — CPU package TDP via Intel RAPL or lm-sensors
  usr_mon_logins    — SSH login attempts (success/failed/invalid-user)

Key implementation details:
- ansipa-checkmk-setup.sh: one-time folder/group creation on FreeIPA
  container; reads automation secret from shared cmk-creds volume;
  stores credentials in IPA dev_mon_base description for clients
- ansipa-checkmk.service: starts after IPA first-boot, retries until
  both IPA and CheckMK are ready
- docker-compose.yml: CheckMK container adds port 8000 (agent receiver
  for push mode); command override writes automation.secret to shared
  volume, patches cookie_auth.conf for API access, and polls
  /cmk-creds/push-mode-*.req to configure push-agent hosts in hosts.mk
- ansipa-enforce-policies.sh: local check scripts for all 5 groups;
  cmk-agent-ctl registration + auto push timer when push mode is active
- CheckMK REST API path corrected to /cmk/check_mk/api/1.0/ (not
  /cmk/api/1.0/ which returns 404 in 2.3)
- .gitignore added to prevent accidental commit of .env test creds

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DHops6PU4c2Mv5UyhUj8Ms
2026-07-01 18:32:28 +02:00
Amir Alexander Abdelbaki 76c3c2c1c5 feat(ansipa): add usr_smb_* and dev_ssh_* policies
usr_smb_r_<name> / usr_smb_rw_<name> (user groups):
- Server creates /data/smb-shares/<name>/ with smb-<name> system user,
  setgid directory, and Samba share stanzas [usr-<name>-r/rw] on startup
- Auto-generates credentials and stores them in IPA group descriptions
  (cifs://<host>:<samba-user>:<password>) so no out-of-band secret distribution
- Client mounts //ipa/<share> at ~/‹name› for logged-in members;
  rw membership wins over r when user is in both groups
- Revert: unmounts and rmdir the mount point on group leave

dev_ssh_<userid> (host group):
- Reads IPA user's SSH public keys (ipasshpubkey / --all output)
- Writes them into /home/<userid>/.ssh/authorized_keys in an
  ansipa-managed section (preserves manually-added keys)
- Home dir is created if it doesn't exist (user may not have logged in yet)
- Revert: removes only the ansipa-managed section on group leave
- Enables same-key SSH access from any registered device to enrolled hosts

Also: add cifs-utils + openssh-server to Ansible package list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DHops6PU4c2Mv5UyhUj8Ms
2026-07-01 13:52:26 +02:00
Amir Alexander Abdelbaki 00ce4f5260 fix(ansipa): fix ansipa-fetch-alerts.sh alert delivery and acknowledgment
Three bugs fixed, all confirmed end-to-end in a live FreeIPA + client VM:

1. Wrong `who` column: loginctl is SESSION/UID/USER (col 3), but `who` is
   USER/TTY/DATE (col 1) — the fallback used col 3, delivering to the wrong
   user.  Split into _parse_session_user() with per-source column selection.

2. Delivered to all users: alerts were sent to every active non-root session
   instead of only members of usr_scan-notify.  Added `id -nG` group check
   inside _parse_session_user().

3. smbclient ls path breakage: `smbclient -c "ls alerts\host\"` treats the
   argument as a glob pattern, returning NT_STATUS_NO_SUCH_FILE.  Changed to
   `cd alerts\host; ls` which correctly lists directory contents.

Bonus: track ACKNOWLEDGED set so alerts deleted-from-server in the current
run are not re-downloaded (avoiding a spurious WARN on the same run).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DHops6PU4c2Mv5UyhUj8Ms
2026-07-01 11:37:00 +02:00
Amir Alexander Abdelbaki 65e859b8f9 fix(ansipa): full end-to-end test + multiple bug fixes
Fixes found during a complete archiso → FreeIPA DC → client enrollment →
ansipa policy deployment test cycle (Rocky 9 client VM, FreeIPA in Docker):

arch-autoinstall.sh:
- Add openssh to pacstrap — was missing, breaking headless/automated installs
- Enable sshd at boot and set PermitRootLogin yes for post-install access

ansipa-install-packages.sh:
- Fix broken uninstall logic: three always-true conditions caused every package
  removal to be silently skipped; replace with a state-file-based approach that
  tracks packages installed by ansipa and removes only those when the IPA group
  disappears

ansipa-enforce-policies.sh:
- Add usr_admin policy: creates /etc/sudoers.d/ansipa-usr-admin granting full
  sudo to the FreeIPA usr_admin user group on every enrolled host; reverted
  when the IPA group is deleted
- Add usr_prt_<printer> policy: auto-adds CUPS printers for FreeIPA user group
  members at login; printer URI stored in IPA group description; per-user ACL;
  reverted when group is deleted or membership ends

deploy-ansipa-policies.yml:
- Document usr_admin and usr_prt_* policies in header comment
- Install cups package on clients (required for printer policy)

FreeIPA container (image/):
- Add docker-env.service: extracts IPA_*/SMB_*/LUKS_*/KEYCLOAK_* env vars from
  /proc/1/environ into /etc/container.env on container start; services read from
  there rather than relying on PassEnvironment (which is lost on container restart)
- Add run.sh: wrapper that starts FreeIPA with --cgroupns host, required on cgroup
  v2 hosts (WSL2, recent Linux) because Docker Compose schema does not expose
  cgroupns_mode; also wires LUKS_KEY_UPLOAD_PASSWORD which the SMB service needs
- Dockerfile: copy + enable docker-env.service; add glibc-langpack-en for locale
- ansipa-smb.service: use EnvironmentFile=/etc/container.env (via docker-env.service)
  instead of PassEnvironment; add docker-env.service dependency
- ipa-first-boot.service: add docker-env.service dependency + EnvironmentFile
- ipa-first-boot.sh: add --skip-mem-check (container has limited cgroup memory
  visibility); remove --no-reverse from non-DNS path (was invalid without DNS)
- docker-compose.yml: add prominent cgroupns note explaining when to use run.sh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DHops6PU4c2Mv5UyhUj8Ms
2026-07-01 10:30:06 +02:00
Amir Alexander Abdelbaki 2a54a0302b fix(hyprland,niri): correct Vicinae theme + config deployment
Same Vicinae theming bug as hyprlua, now fixed across the remaining DE modules:

- Vicinae loads named custom themes only from ~/.local/share/vicinae/themes/*.toml,
  not ~/.config/vicinae/. Both modules now deploy cyberqueer.toml there so
  settings.json's theme.dark.name="cyberqueer" resolves instead of falling back
  to the stock dark theme.

- hyprland: the source had settings.json double-nested at vicinae/vicinae/, so it
  deployed to ~/.config/vicinae/vicinae/settings.json and was never read. Flattened
  the source to vicinae/{settings.json,cyberqueer.toml} (matching hyprlua), so it
  now lands at ~/.config/vicinae/settings.json.

- niri/vicinae is a symlink to hyprlua/vicinae and niri.sh uses `cp -rL`, so its
  settings.json was already correct; only the theme-deploy line was needed.

Verified by simulating both deploys into a temp HOME: final layout matches the
known-good hyprlua result.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
2026-06-27 23:32:20 +02:00
Amir Alexander Abdelbaki 734e28b1fc fix(hyprlua): deploy Vicinae theme where Vicinae actually scans for it
The config-copy step put cyberqueer.toml into ~/.config/vicinae/, but Vicinae
loads NAMED custom themes only from ~/.local/share/vicinae/themes/*.toml (per
the official docs). So settings.json's theme.dark.name="cyberqueer" never
resolved and the launcher silently fell back to the stock dark theme.

Copy the theme into ~/.local/share/vicinae/themes/ so the named theme resolves
and the cyberqueer palette is applied.

Note: the hyprland and niri DE modules have separate, pre-existing Vicinae
layout issues (hyprland double-nests settings.json; niri bundles no vicinae
config) — left for a dedicated follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
2026-06-27 23:09:01 +02:00
Amir Alexander Abdelbaki 4332abde35 fix(core-packages): make PipeWire the jack/pulse provider to avoid DE conflict
core-packages installs ffmpeg, which depends on the virtual `jack`. With only
`pipewire` (not pipewire-jack) requested, pacman --noconfirm picks the default
provider `jack2`. The DE modules then install `pipewire-jack`, which conflicts
with the already-installed jack2 — pacman can't auto-resolve it and aborts the
whole DE module ("pipewire-jack and jack2 are in conflict").

Explicitly list pipewire-jack/pipewire-pulse/pipewire-alsa in core-packages so
PipeWire is the chosen provider from the start; jack2 is never pulled and the DE
package install no longer conflicts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
2026-06-27 13:25:49 +02:00
Amir Alexander Abdelbaki 8bf778e487 fix(modules): resolve rust/rustup conflict and greetd config-dir abort
Two more module aborts surfaced by the live VM test (both pre-existing, only
unmasked once the wprs bug was fixed and the transaction got further):

1. core-packages.sh listed BOTH `rust` and `rustup`. They conflict (both provide
   cargo/rustc); pacman --noconfirm cannot auto-resolve it and aborts the whole
   core-packages transaction ("unresolvable package conflicts"), so wget and the
   other core tools never installed. Drop `rust` — the setup uses rustup
   exclusively (`rustup default stable`).

2. core.sh copied the greetd config into /etc/greetd before that directory
   existed (greetd is pulled in later by the DE module), so the cp failed and
   `set -e` aborted Core Services before fail2ban/udisks2 were enabled. mkdir -p
   /etc/greetd first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
2026-06-27 04:15:36 +02:00
Amir Alexander Abdelbaki a9796bb036 fix(modules): remove invalid wprs from core packages; DEs use curl not wget
Live VM test revealed the HyprLua DE install aborted at the cursor-download step
("wget: command not found", exit 127), leaving ~/.config/hypr undeployed. Two
root causes:

1. core-packages.sh listed `wprs` in the pacman array, but wprs is AUR-only
   (wprs-git). pacman aborts the ENTIRE transaction on an unknown target, so the
   whole core-packages install failed with "target not found: wprs" — and NONE
   of the core packages installed, including wget. Removed wprs (it has its own
   optional wprs.sh AUR module) and documented why it must never be re-added.

2. hyprlua/hyprland/niri downloaded the cursor theme and wallpaper with wget,
   which is not part of the base system. Switched them to curl (always present),
   so the DE install no longer depends on an earlier module having installed wget.

Without these, HyprLua silently half-installed (packages yes, config no) and the
booted system had no Hyprland configuration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
2026-06-27 03:05:11 +02:00
Amir Alexander Abdelbaki 6251eb8218 fix(shell-setup): set default shell without a password prompt
A bare `chsh -s /usr/bin/zsh` authenticates the calling user through PAM and
prompts "Password:", stalling an unattended install right after oh-my-zsh.
Run it via `sudo chsh ... "$(whoami)"` so it uses the passwordless setup sudo
rule and completes silently (also more reliable: it targets the account
explicitly rather than relying on the ambient user).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
2026-06-27 02:13:32 +02:00
Amir Alexander Abdelbaki 9a711013ce fix(shell-setup): install oh-my-zsh non-interactively
oh-my-zsh's install script, even with RUNZSH=no/CHSH=no, still prompts
"Do you want to overwrite it with the Oh My Zsh template? [Y/n]" in interactive
mode — which hangs an unattended/answerfile install at the shell-setup module.

Pass --unattended to skip all prompts, and KEEP_ZSHRC=yes so it preserves the
dotfiles ~/.zshrc symlink (created earlier in this module) instead of replacing
it with the oh-my-zsh template.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
2026-06-27 02:11:55 +02:00
Amir Alexander Abdelbaki 61ff61d432 fix(modules): guard remaining interactive prompts for unattended installs
Audited every module for prompts that would hang an answerfile/unattended run.
The EWW form-factor question and several interactive-config modules were still
unguarded (only hyprlua had been fixed):

- hyprland.sh, niri.sh: skip the EWW form-factor `read` when MARCHY_UNATTENDED=1
  or stdin is not a TTY, defaulting to the desktop/no-battery bar (matches hyprlua).
- mail-notmuch.sh, caldav-sync.sh: install the tools, then exit cleanly in
  unattended mode instead of blocking on the account/server credential prompts —
  the user configures the account after first boot.
- freeipa-server.sh: bail out early in unattended mode; FreeIPA server provisioning
  is interactive and must run on a booted system (ipa-server-install needs running
  services), so it can never run during the install.

freeipa-client.sh is left as-is: it has a genuine --unattended enrolment path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
2026-06-27 02:09:04 +02:00
Amir Alexander Abdelbaki 587b95cada fix(installer): make the unattended module path non-interactive
An answerfile/PXE install runs the TUI modules with no operator present, so any
interactive prompt or confirmation hangs the deployment forever.

- tui-install.sh: run each module in the FOREGROUND with a background `tail -f`
  mirroring its log, instead of backgrounding the module. Backgrounding made
  interactive module reads (e.g. the EWW form-factor question) trigger SIGTTIN
  and stop; the file-based log still avoids the `| tee` pipe-inherit hang that
  paused installs after a module spawned a daemon. Export MARCHY_UNATTENDED=1
  in answerfile mode so modules can detect it.
- hyprlua.sh: skip the EWW form-factor `read` when MARCHY_UNATTENDED=1 or stdin
  is not a TTY, defaulting to the desktop/no-battery bar; add --noconfirm to the
  tablet-branch yay call.
- python.sh: add --noconfirm --needed to its `pacman -Syu` so it no longer waits
  on the "Proceed?" prompt during unattended installs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
2026-06-27 01:15:43 +02:00
Amir Alexander Abdelbaki d445f965ce fix(modules): make service/session ops chroot-safe to prevent install aborts
When the TUI modules run inside the archiso installer chroot, the new
system's systemd is not the running init and there is no user session bus.
Operations like `systemctl start`, `enable --now`, `systemctl --user`, and
`gsettings` fail there and, under `set -e`, abort the whole module.

- logging.sh: add in_chroot/have_user_bus/enable_service/start_service
  helpers. enable_service warns instead of aborting; start_service skips in
  a chroot (unit starts on first boot via its enable symlink).
- core.sh, hyprland.sh, hyprlua.sh, niri.sh: route enables through
  enable_service, starts through start_service, and guard gsettings behind
  have_user_bus. Fixes the cronie-enable failure and the ly/DM setup abort.
- app modules (tlp, timeshift, open-webui, mysql, qemu, ollama, cockpit,
  docker, ssh-server): convert `enable --now`/plain enables to
  enable_service + start_service so they no longer abort during chroot install.
- tui-install.sh: run modules with output to a file plus a pid-bound tail,
  waiting on the module PID, so a daemon child inheriting the pipe can no
  longer hang the installer after a module (e.g. flatpak) finishes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5kHioUMK3mtf2eiLEozCM
2026-06-26 20:58:37 +02:00
Amir Alexander Abdelbaki bc13de7508 feat(installer): global flatpaks, drop redundant TUI hostname, upfront passwords
- Flatpaks now install globally again: ensure_flatpak adds the Flathub remote at
  --system and all 19 app modules use `sudo flatpak install --system`. Running
  via sudo (root) performs the system op directly, avoiding the SystemHelper/
  polkit D-Bus path that caused "The name is not activatable" for non-root users.

- tui-install.sh no longer prompts for or sets the hostname — the base installer
  already configures it. Removed the Hostname section, the MAC-suffix helper, the
  AF_HOSTNAME field and the summary line.

- archbaseos-guided-install.sh now gathers ALL input up front, including
  passwords. New ask_password() prompts in clear text (by request) and requires a
  confirmation entry, looping until the two match — so each password is typed
  exactly twice and never again. The LUKS passphrase is captured once and fed to
  luksFormat/open/luksAddKey (--key-file=-) and cryptenroll ($PASSWORD), instead
  of cryptsetup prompting repeatedly. After all input, a single all-caps "type
  YES" gate replaces the old per-step confirmations (answerfile mode keeps its
  5-second abort window). The run-TUI choice is also asked up front.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 18:40:49 +02:00
Amir Alexander Abdelbaki 9107b9961a fix(installer): root-owned ~/.config, wrong logging path, flatpak system scope
Diagnosed from a full guided-install log plus a Hyprland startup log. Three
distinct remaining failures:

1. ~/.config left root-owned. The FIDO/Yubico setup runs `mkdir -p
   ~/.config/Yubico` as root (creating ~/.config itself), then chowned only
   Yubico/. ~/.config stayed root-owned, so every later user step failed with
   EACCES: shell-setup symlinks (starship.toml), the mail/caldav systemd --user
   timers, and Hyprland creating ~/.config/hypr at startup. Chown the whole
   ~/.config in both Yubico spots, and defensively reclaim it in shell-setup.

2. python/wprs/plymouth/zfs sourced ../lib/logging.sh, but apps/ modules need
   ../../lib — so they aborted with "No such file or directory". Corrected.

3. Flatpak app modules ran `flatpak install -y` at system scope, which needs the
   Flatpak SystemHelper D-Bus service + polkit (unavailable in a chroot/TTY
   install) — the "The name is not activatable" failures (wireshark, xournal,
   rnote, firefox-browser, …). Switch ensure_flatpak and all 19 main-flow
   installs to --user scope, matching apply_flatpak_theme's --user overrides.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 18:27:04 +02:00
Amir Alexander Abdelbaki 2c05c4e274 fix(installer): repair broken package lists and make user-timer setup non-fatal
The core, hyprlua and hyprland package installs used backslash-continued
`pacman` commands with inline `#` comments. Bash treats the first inline
comment as the end of the command, so pacman ran with only the packages
before it and every later name (7zip, cronie, nwg-dock-hyprland, …) was
executed as a shell command — failing under `set -e`. Move all three lists
into arrays, where per-item comments are valid, and install with `--`.

Also:
- himalaya: install the official `himalaya` package (AUR `himalaya-bin` is gone).
- mail-notmuch / caldav-sync: make the systemd *user* timer setup and the
  initial sync best-effort. A bare TTY/chroot install has no user session bus
  (and ~/.config may not be writable yet), so `systemctl --user` and the mkdir
  could abort the module; warn and continue instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 18:20:21 +02:00
Amir Alexander Abdelbaki 0ab535f772 fix(installer): install OpenDeck from AUR instead of Flatpak
opendeck is not in the official repos, so install the native AUR package
with yay (matching the repo's other AUR modules) and drop the Flatpak
path. Wire `opendeck` into Hyprland autostart instead of
`flatpak run com.mairtech.OpenDeck`, and update the idempotency guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 16:04:13 +02:00
Amir Alexander Abdelbaki 5647852ee6 refactor(modules): move plymouth into apps/, retire freeipa-image to tools/
apps/ is for modules that install software during setup. freeipa-image is
support tooling for the ansipa controller, run manually before installation,
so it has no place in the TUI module picker.

- git mv optional-Modules/plymouth.sh → apps/plymouth.sh
- git mv apps/freeipa-image.sh → setup/tools/freeipa-image.sh
- modules.conf: add plymouth (default=on, excludes=plymouth-custom); remove freeipa-image
- generate-modules.sh: regenerate all sentinel regions (81 → 81 active modules,
  freeipa-image dropped from checklist/summary/dispatch, plymouth added with on default,
  conflict block gains plymouth ↔ plymouth-custom pair)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 13:30:03 +02:00
Amir Alexander Abdelbaki 5d7c97409b feat(installer): modules.conf registry + sentinel-based code generation
Collapse 5 manual touch points per optional app module (counter, checklist,
summary, conflict check, dispatch in tui-install.sh + answerfile generator +
docs table row) into a single source of truth at setup/modules.conf.

- setup/modules.conf: 80-module registry (id|category|description|default|excludes)
  with sensible defaults (python, firefox-browser, onlyoffice) and conflict pairs
  (plymouth-custom ↔ plymouth)
- setup/sync-modules.sh: scans apps/*.sh, stubs any IDs missing from modules.conf
- setup/generate-modules.sh: regenerates all sentinel regions from modules.conf
  (supports --dry-run); fixes Python re.sub backslash-n corruption via lambda repl
- tui-install.sh: 5 sentinel regions added (module-counters, module-checklist,
  module-summary, module-conflicts, module-dispatch); fixes 19 modules missing
  from count_steps() and mail-notmuch/caldav-sync missing from SUMMARY
- generate-answerfile.sh: module-checklist sentinel; list-height now auto-computed
- docs/md/modules.md: per-category sentinels; all sections regenerated from conf
- Renames: prismlauncher→prism, freeipa-image-builder→freeipa-image,
  firefox→firefox-browser, zed→zed-ide; moves python/zfs/wprs into apps/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 13:24:02 +02:00
Amir Alexander Abdelbaki 9289f01965 feat(ansipa): unify FreeIPA group naming with dev_* / usr_* prefixes
All ansipa host/user group names now follow a consistent prefix scheme:

  dev_mod_<name>          — dotfiles module install (was ansipa-module-)
  dev_fp_<app-id>         — Flatpak install (was fp_install_)
  dev_pkg_<package>       — native package install (was ansipa-install-)
  dev_daemon-enable-<u>   — service enable policy (was policy-daemon-enable-)
  dev_daemon-disable-<u>  — service disable policy (was policy-daemon-disable-)
  dev_timeshift-backup    — backup policy (was policy-timeshift-backup)
  dev_security-scan       — scan policy (was policy-security-scan)
  dev_no-local-users      — auth lockdown (was no_local_users)
  dev_local-sudo-<user>   — per-device sudo grant (was local_sudo_)
  usr_block-binary-<name> — per-user binary block (was policy-block-binary-)
  usr_scan-notify         — per-user alert notification (was policy-scan-notify)

Also adds a JSON state manifest (manifest.json) to ansipa-install-modules
and tightens the FreeIPA enrollment guard to check /etc/ipa/default.conf.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LcnnA1whUwQkDv1omsgh9Y
2026-06-26 11:48:24 +02:00
Amir Alexander Abdelbaki 2be85739b5 feat(apps): add Tangent Notes module (Flatpak)
Installs io.github.suchnsuch.Tangent via Flatpak with cyberqueer theme
applied. Registered in TUI installer, answerfile generator, and docs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 11:20:25 +02:00
Amir Alexander Abdelbaki e278795610 feat(apps): add Obsidian module (Flatpak)
Installs md.obsidian.Obsidian via Flatpak with cyberqueer theme applied.
Registered in TUI installer, answerfile generator, and docs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 11:15:08 +02:00
Amir Alexander Abdelbaki 394927573d feat(apps): add Rnote module (Flatpak)
Installs com.github.flxzt.rnote via Flatpak with cyberqueer theme
applied. Registered in TUI installer, answerfile generator, and docs
alongside xournal++ in the Productivity section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 11:11:19 +02:00
Amir Alexander Abdelbaki 66e51474a1 feat(apps): add OpenDeck + ydotool module
Installs ydotool via pacman and OpenDeck via Flatpak, wires ydotoold
and OpenDeck into the Hyprland autostart. Registers the module in the
TUI installer, answerfile generator, and docs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 11:08:32 +02:00
Amir Alexander Abdelbaki 29dc304e4f remigrating to custom monitor manager - setup reverted 2026-06-26 11:00:51 +02:00
Amir Alexander Abdelbaki f5a1182a3d feat(plymouth): add custom-logo variant, sync answerfile + docs
- plymouth.sh: accepts PLYMOUTH_LOGO_SRC env var; PNG used as-is, SVG
  converted via rsvg-convert (librsvg only installed when needed)
- apps/plymouth-custom.sh: thin wrapper that validates the caller-supplied
  path and delegates to plymouth.sh with PLYMOUTH_LOGO_SRC exported
- install-modules.sh: adds 'Plymouth (custom)' checklist entry; prompts
  for image path via inputbox before the confirmation dialog; exports
  PLYMOUTH_LOGO_SRC into the module run
- generate-answerfile.sh: adds 'plymouth' (on by default) to the
  components checklist to match tui-install.sh
- docs: installation.md and modules.md updated with Plymouth component,
  answerfile schema, mkinitcpio note, and custom-logo module entry

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyBNiWy3wpawrWb9ryVk7p
2026-06-26 10:57:32 +02:00
Amir Alexander Abdelbaki 52fe3a910e feat(plymouth+resources): bundle bg-skull.svg in repo and archiso
Move the splash logo into resources/bg-skull.svg so it's tracked in git
and always available alongside the dotfiles.  build.sh now copies
resources/ into /root/installer/resources/ on the ISO.  The Plymouth
module resolves the SVG from the repo copy first, ISO copy second —
no user intervention or ~/Pictures setup required.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyBNiWy3wpawrWb9ryVk7p
2026-06-26 10:48:45 +02:00
Amir Alexander Abdelbaki fe72a4c71b feat(plymouth): add M-Archy boot splash module with skull logo + spinner
Installs a custom Plymouth theme (m-archy) with bg-skull.svg converted
to PNG (Plymouth is PNG-only via libpng — no SVG support) and a 12-dot
magenta spinner animation. Enabled by default in tui-install.sh; also
available as an optional module in install-modules.sh. Archiso image
remains Plymouth-free.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyBNiWy3wpawrWb9ryVk7p
2026-06-26 10:44:02 +02:00
Amir Alexander Abdelbaki cb464c830f chore: add inline comments across all modules and configs 2026-06-25 13:07:06 +02:00
Amir Alexander Abdelbaki bc564f9b83 screw the monitor manager script, i'm using an off-the-shelf solution 2026-06-24 18:50:55 +02:00
Amir Alexander Abdelbaki dd1cd2b8c7 feat(setup/apps): convert graphical apps to Flatpak-first with cyberqueer GTK theme
Policy change: graphical apps now prefer Flatpak > pacman > AUR. Non-graphical
tools keep pacman > AUR > source. This makes installed apps sandboxed, keeps
system packages clean, and gives us a single hook point (apply_flatpak_theme)
to theme every GUI app consistently.

lib/logging.sh — two new helper functions sourced by every module:

  ensure_flatpak()
    Checks if flatpak is installed (pacman installs it if not) and ensures the
    Flathub remote is registered. Called at the top of every Flatpak script so
    the module is self-contained and safe to run in any order.

  apply_flatpak_theme(app_id)
    Copies gtk-themes/cyberqueer/ from the Dotfiles repo into ~/.themes/, then
    calls `flatpak override --user --filesystem=~/.themes:ro <id>` so the
    sandbox can read it, and `flatpak override --user --env=GTK_THEME=cyberqueer
    <id>` to activate it. Gracefully skips with a warning if the theme source
    directory is absent.

App scripts converted (pacman/AUR → Flatpak + theme):
  ardour       org.ardour.Ardour
  audacity     org.audacityteam.Audacity
  chromium     org.chromium.Chromium
  firefox      org.mozilla.firefox
  geany        org.geany.Geany
  gimp         org.gimp.GIMP
  inkscape     org.inkscape.Inkscape
  kate         org.kde.kate
  kdenlive     org.kde.kdenlive
  krita        org.kde.krita
  librewolf    io.gitlab.librewolf-community.librewolf
  lmms         io.lmms.LMMS
  localsend    org.localsend.localsend
  min-browser  com.github.minbrowser.min
  mixxx        org.mixxx.Mixxx
  onlyoffice   org.onlyoffice.desktopeditors
  openshot     org.openshot.OpenShot
  rdp-client   org.remmina.Remmina  (was pacman remmina + freerdp + libvncserver;
               Flatpak bundles all protocols, including VNC and SSH tunnels)
  shotcut      org.shotcut.Shotcut
  steam        com.valvesoftware.Steam
  vscodium     com.vscodium.codium
  wireshark    org.wireshark.Wireshark
  xournal      com.github.xournalpp.xournalpp
  zed          dev.zed.Zed
  zen-browser  io.github.zen_browser.zen

Special cases:

  blender-povray: Blender → Flatpak (org.blender.Blender) + theme; POV-Ray
    stays pacman because it has no Flatpak and is a CLI renderer, not a GUI app.

  prismlauncher / stuntrally: were already Flatpak installs; added
    apply_flatpak_theme so they pick up the cyberqueer theme like everything else.

  vesktop: switched from AUR vesktop to Flatpak dev.vencord.Vesktop. The AUR
    build requires cargo and takes several minutes; the Flatpak is pre-built.
    Vencord config is now deployed to ~/.var/app/dev.vencord.Vesktop/config/
    (both Vencord/ and vesktop/ sub-dirs) instead of ~/.config/, which is where
    the Flatpak sandbox exposes its config directory.

  k8s: kubectl stays pacman (it is a CLI tool with no GUI, no Flatpak needed);
    podman-desktop switches from pacman podman-desktop to Flatpak
    io.podman_desktop.PodmanDesktop + theme, because it is a full GUI app.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 10:19:06 +02:00
Amir Alexander Abdelbaki 7fd0e37061 refactor(hyprlua): move hypr-usr/ into hypr/usr/ and wire sysupdate sync
Device-specific Lua configs (monitors, binds, input, etc.) previously lived
in a top-level hypr-usr/ sibling to hypr/. They now live inside hypr/usr/
so all hypr-related configs sit under one directory tree.

- git mv hypr-usr/ → hypr/usr/
- hyprland.lua: require() calls updated to require("usr.*")
- input.lua: require("input-device-exceptions") → require("usr.input-device-exceptions")
- hyprpaper.conf + monitorhandler.sh + wallpaper-picker: wallpaper.conf path
  updated from ~/.config/wallpaper.conf → ~/.config/hypr/usr/wallpaper.conf
- monitorhandler.sh: fix stale desktopenvs/hyprland/ → desktopenvs/hyprlua/
- binds.lua: edit-binds keybind path corrected to ~/.config/hypr/usr/binds.lua
- helpmenu.sh: update binds.lua source path
- updater.conf: SOURCE_BASE fixed (hyprland→hyprlua), config hypr except usr,
  remove flat hypr-usr entry
- update-configs.sh: add "except <subdirs>" support for the config type
- hyprlua.sh installer: remove now-redundant hypr-usr copy lines
- migrate-hyprland-to-hyprlua.sh: deploy from hypr/usr/ instead of hypr-usr/
- sysupdate.sh: add CONFIG SYNC section with _migrate_hypr_usr() which moves
  old flat-layout files into usr/ (case 1) or copies fresh from dotfiles when
  no local config exists (case 2); fallback copy preserves hypr/usr/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 15:46:01 +02:00
Amir Alexander Abdelbaki 9638fd337a feat(hyprlua/niri): webcam presence detection + systemd-inhibit idle/caffeine
- Add presence-detect.sh daemon: checks webcam every 2 min via OpenCV haar
  cascade (presence_detect.py); holds systemd-inhibit --what=idle lock while a
  face is detected so hypridle never fires during an active session
- Add enroll-biometrics.sh: Cyberqueer dialog TUI for camera configuration/test
  and howdy face auth enrollment (add/list/remove/test models)
- Rewrite caffeine.sh (hyprlua + niri): replace kill/restart of idle daemon with
  systemd-inhibit --what=idle:sleep + PID file; idle daemon stays running
- Fix hypridle.conf: correct ;; → ; in after_sleep_cmd, add
  ignore_dbus_inhibit=false, bump lock timeout 120s→180s to account for the
  2-min presence detection cycle
- Wire Super+Shift+B keybind for enrollment TUI in both hyprlua and niri
- Fix niri/scripts/python: was real dir with inner symlink causing cp -rL to
  create a nested python/python/ hierarchy; replaced with direct symlink
- Add python-opencv and v4l-utils to hyprlua + niri installers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 14:50:36 +02:00
Amir Alexander Abdelbaki 814080cc04 feat(installer): install evdev-rce for touchscreen long-press right-click
Reverts incorrect touch_long_press_time option (not a real Hyprland setting).
Adds evdev-right-click-emulation install + systemd enable to the tablet (T)
eww bar selection in the hyprlua installer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 15:43:48 +02:00