Commit graph

3 commits

Author SHA1 Message Date
emirhanmamak-blueforce
da460519e8 feat(install): internet-free install flow, auto-discovered local packages
The operator no longer answers an "offline?" question and no longer needs
network access: the installer finds the package set that ships with the
checkout, so the only input left is the dealer number.

Installer
- Auto-discover the local APT repository (BF_OFFLINE_REPO, then
  provisioning/offline-repo/built, then /opt/blueforce/offline-repo) and
  enter offline mode on its own; --offline stays for compatibility.
- Fail closed with an explicit Turkish message when nothing is found: the
  searched locations, what to do, and a named exit reason -- no state touched.
- Distinguish "repository absent" (operator error, actionable) from
  "repository present but invalid" (OFFLINE BLOCKED, verification gate).
- Network is informational only in precheck/network modules; no probe runs in
  offline mode and archive.ubuntu.com is no longer mandatory.

ISO builder
- Look for built/ first, which is what build-index.sh produces, and accept
  build/ as a warned legacy path. The two disagreed, so a medium could be
  built without the package set and fail only at install time.

Docs
- docs/27, scripts/install/README, provisioning/offline-repo/README and
  provisioning/firstboot/README describe the discovered flow, the two failure
  modes, and that the installer downloads nothing.

Tests
- check-admin-static exercised the "console missing from the clone" branch by
  deleting a tracked file; git clone restored it from history, so the branch
  never ran and the warning was effectively untested. Build the stripped
  source as a real commit instead; verified by mutation.
2026-09-16 16:53:24 +03:00
emirhanmamak-blueforce
1387186b03 feat(bfos): Python operator console, Turkish UI, committable offline packages
Switches the operator path from bash-only to a Python console, keeps the bash
menu as a fallback, and makes the offline package set travel with the repository
so the person installing a device never downloads anything.

bfos/ (stdlib only, no textual/rich: those would need a download, which defeats
the offline goal):
- simple mode offers three choices (Kur / Durum / Çıkış); the detailed mode with
  all 21 fleet operations and the local installer is reachable from the menu or
  with --full, so a first-time operator is not buried in options
- gate parity with the bash console: scope read from the playbook file and
  refused on mismatch, production needs a typed token plus a separate approval,
  release-gated runs need their wave name typed, destructive runs need a second
  approval, "all" walks wave by wave and stops at the first failure
- Turkish throughout; audit log at /var/log/blueforce-console.log (600, falls
  back to the user state dir) with a single redaction choke point
- bfos/ui_gui.py adds an optional tkinter window over the same operations layer;
  headless and missing-tkinter cases explain themselves in Turkish

admin/bf-bootstrap.sh: links the Python console as `bf` (primary) and keeps
`bf-menu` (fallback), reports both, and recommends `sudo bf`.

Offline packages are committable now (this was the blocking contradiction):
- .gitignore ignored `Packages`, `Packages.gz` and `packages.lock.tsv` everywhere,
  so a built bundle could never be committed and the installer would always have
  been told to download. provisioning/offline-repo/built/ is now allowed through
  while scratch output stays untracked
- provisioning/offline-repo/{add-packages.sh,build-index.sh,repo-manifest.yaml}
  plus a Turkish step-by-step README for the repository owner
- the over-broad `*token*` ignore rule is narrowed to `*[-_.]token*` so ordinary
  filenames are not silently excluded while token files still are

Tests: run-all.sh now runs the Python suites too (previously it collected only
*.sh, so 212 Python assertions were silently skipped) and executes from the repo
root. 11/11 pass, exit 0.

Two test expectations were corrected rather than the code: a read-only
wave-scoped report legitimately asks for no gate token (the bash console never
did either), so that assertion now checks the command carries operation_wave and
operation_confirmed, and a new test asserts release-gated runs still require
their token. The bootstrap completion contract moved from `sudo bf-menu` to
`sudo bf`.
2026-09-16 16:15:27 +03:00
emirhanmamak-blueforce
f8cdd1ed01 feat(admin): operator console, git-based install, optional credential delivery
Simplifies the primary path from "build a custom ISO" to "install from git and
run one menu", which is what the operator actually asked for. The ISO tooling
stays in the tree as a secondary/optional route and is not removed.

admin/bf-bootstrap.sh -- one command on Ubuntu:
- clones the repo to /opt/blueforce-linux or fast-forwards an existing checkout
  (a dirty tree is reported and skipped, never reset or stashed)
- installs git/curl/ansible-core, links admin/bf-* and scripts/*/bf-* into
  /usr/local/bin, and finishes with the exact next command
- --check is read-only, works without root, and mutates nothing; the piped form
  (curl ... | sudo -S -p '' bash) is guarded against eating the script

admin/bf-menu -- numbered operator console (categories 1-5):
- 1 installs THIS device locally; 2 and 5 drive the fleet through the existing
  21 Ansible playbooks; 3 prepares and verifies central integration without
  installing a central server; 4 holds local tools
- every operation picks one target: a dealer number or an approved wave group.
  Scope is derived from the playbook file itself, so a playbook whose declared
  scope disagrees with the console's table is refused rather than run
- the command is always shown before it runs; release-gated operations require
  a typed wave token, production requires a second token, and "all" runs
  wave-by-wave, stopping at the first failure
- audit log at /var/log/blueforce-console.log (mode 600, falls back to
  $XDG_STATE_HOME when not writable), recording sanitized target/command/exit
  only: no playbook output, no secrets

admin/bf-creds -- optional credential record, DEFAULT OFF:
- a bare run generates nothing and reports what already exists (device id,
  WireGuard public key, private key path only, RustDesk id)
- --generate writes /root/blueforce-credentials.txt (0600) and --fetch delivers
  it to admin/secrets/<dealer>.txt (0700/0600, git-ignored). The secret never
  reaches stdout, stderr or argv
- this does not change the SSH design: password auth stays off and the
  blueforce account stays locked, per docs/06. It is a local delivery aid only

docs/33-OPERATOR-CONSOLE.md -- step-by-step guide for an operator new to Linux
and DevOps: which command to type where, what the screen will ask, both
end-to-end sample sessions, the five most common errors, and the security rules.

Tests: run-all.sh -> 9/9 pass, exit 0 (adds check-admin-static.sh, 159 checks,
with negative controls and mutation-proven non-vacuity).
2026-09-16 15:26:17 +03:00