Commit graph

26 commits

Author SHA1 Message Date
25353f548d fix(dev-env): confirm before force-deleting an unmerged PR branch
git-pr-cleanup (prc) tried `branch -d` then fell back to `branch -D`
unconditionally, silently destroying an unmerged branch when prc was
run before the PR merged or against the wrong branch. Keep the safe
`-d` for the normal post-merge path, but prompt before forcing so
unmerged commits aren't lost without consent; decline keeps the branch.
2026-06-20 09:52:17 +02:00
cd95974e48 fix(dev-env): count deleted backups in the parent shell, not a subshell
cleanup_backups piped `git branch --list` into `while read`, so the
`deleted` counter incremented inside a pipeline subshell and never
propagated. The closing "Deleted N backup branch(es)" always reported 0,
however many were actually removed. Switch the inner loop to process
substitution (matching the outer find_forked_repos loop) so the count
survives.
2026-06-20 09:51:31 +02:00
11cf5aa0cf docs(claude): drop the deleted dev up CLI from seeded orientation files
The matured dev-env backport removed dev.sh (which provided
`dev up/down/logs/shell`) but the seeded CLAUDE.md files still told
users — and Claude sessions, since these symlink into ~/dev/ — to run
those commands. `dev` is now just a nav function (cd $DEV_ROOT), so
`dev up` silently cd'd and started nothing.

Rewrite both Quick-commands / Default-dev-workflow sections to the real
command set (lb/prb/dev-status/regtest-start/dev-deploy) and document
that FakeWallet startup is manual — there is no `dev up` wrapper.
2026-06-20 09:50:39 +02:00
3d73e02d6b docs(readme): reflect the now-real dev-env tooling
The root README pitched a unified 'dev up' CLI and called the dev-env
stubs — both stale now that the real implementation has landed. Rewrite
the status header, the day-to-day command set, the module file tree, and
drop the (planned)/(stub) markers on bootstrap, prb, and nav helpers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 21:18:56 +02:00
e38d313db2 feat(dev-env): backport matured dev-env implementation from /etc/nixos
Replace the stub dev-env with the real, working implementation that grew
in the reference machine config — de-identified for the public scaffold.

Nix layer:
- options.nix: full project schema (url/upstream/fork/category/
  worktreeRoot/worktrees{branch,path,remote}/isClone/deployFlakeInput),
  deploy.targets, github.forkUser, writeDirenvHints. Drops the
  forgejo-URL block + deploy-flake auto-derivation (incoherent in a
  scaffold that uses explicit per-project urls).
- lib.nix: mkProject + worktreePath/bareRepoPath/projectRemotes,
  generalized to the explicit-url model (origin falls back to upstream).
- config.nix: renders /etc/dev-env/{config.sh,projects.json,
  tmux-sessions.json}, installs helpers via writeShellScriptBin, loads
  shell functions into interactive shells, wires the git pre-commit hook.

Scripts (config-driven, read /etc/dev-env at runtime):
- bootstrap.sh, nav.sh, worktree.sh, pr-helpers.sh, rebase.sh,
  status.sh, deploy.sh, regtest.sh, tmux-launch.sh.
- Stripped aiolabs/forgejo/bitspire/lamassu/webapp hardcoding; the
  github-fork remote is renamed 'fork' to match git.remotes vocabulary.
- Removes the dev.sh stub (the matured impl uses discrete commands +
  shell functions, not a unified 'dev' CLI).

presets/example.nix: a worked, generic project list replacing the
identity-specific aiolabs preset. tests/smoke.nix + flake checks
exercise the schema; 'nix flake check' is green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 21:18:49 +02:00
773632562e feat(dev-env): wire shared pre-commit secret scanner via core.hooksPath
Ships `modules/dev-env/scripts/git-hooks/pre-commit` — the same
secret-scanner pattern omnixy uses, lightly adapted (drops the
omnixy-specific test_auth.py skip, generic header comment).

New option `lnbits-sensei.devEnv.gitHooks.enable` (off by default).
When on, modules/dev-env/config.nix installs the hook at
`~/.local/share/lnbits-sensei/git-hooks/pre-commit` and sets the
consumer's git `core.hooksPath` to that directory, so every repo on
the machine picks it up without per-repo wiring.

The hook refuses to commit obvious secrets (PRIVATE KEY blocks,
`password=…`, `secret=…`, `api_key=…`, `admin_key=…`, AWS keys,
non-placeholder POSTGRES_PASSWORD) and unencrypted sops files
(checks for a top-level `sops:` block AND `mac: ENC[…]` — either
signal alone is forgeable). False positives are handled via
`# pragma: allowlist secret` line- or block-level markers (gitleaks
convention).

docs/secrets-management.md gets a new subsection covering what the
hook does, when to enable it, and the false-positive escape hatches.
The Pitfalls section's reference to "the pre-commit hook most
consumers use" is replaced with a concrete pointer to this option.

`nix flake check --no-build` stays green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 09:21:27 +02:00
fc1d31244a docs(secrets): add beginner walkthrough for sops-nix
New docs/secrets-management.md walks through getting secrets out of
.env and into sops-encrypted YAML, assuming zero familiarity with
sops or age. Companion to the wiring landed in the prior commit.

Sections:

- Why bother — the .env failure modes (history leaks, plaintext on
  disk) and what sops/age fixes.
- What's in the box — pointers to the existing scaffold (flake input,
  modules/secrets.nix, .sops.yaml, .gitignore guards), why it's inert
  until the first encrypted file lands.
- Step-by-step — install tools, generate the age key (with a clear
  warning about back-ups + no recovery), paste the public key into
  .sops.yaml, create the first encrypted file via `sops`, declare
  secrets in NixOS, reference via config.sops.secrets.<name>.path,
  activate.
- Common operations — edit / view / rotate / updatekeys.
- Multi-host server deployment — per-host age keys at
  /var/lib/sops-nix/key.txt, path_regex-scoped recipients in
  .sops.yaml so each host only decrypts its own secrets.
- Pitfalls — don't commit unencrypted YAML, don't lose the key,
  updatekeys ≠ rotation, sops -d outputs are sensitive, the
  pathExists gate fails-silently-on-delete trap.

Linked from README "Further reading" with a one-liner noting the
sops-nix wiring already ships with the scaffold.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:47:19 +02:00
7af3bce544 feat(secrets): scaffold sops-nix for declarative secrets
Wires sops-nix as a flake input and bakes the NixOS module into
configuration.nix via modules/secrets.nix. Per-host defaults live in
modules/secrets.nix:

- defaultSopsFile = ../secrets/${settings.hostName}.yaml
- defaultSopsFormat = yaml
- age.keyFile = /home/${settings.user}/.config/sops/age/keys.txt

The whole sops block is gated on `builtins.pathExists` so flake eval
succeeds before the encrypted file is created — important during the
scaffold-bootstrap phase where the consumer hasn't yet generated an
age key.

Adds .sops.yaml with a placeholder admin recipient (overwrite with
your real age public key before encrypting anything) and a
creation_rules block matching `secrets/*.yaml`.

.gitignore loosened so `secrets/*.yaml` and `secrets/README.md` can
be checked in while plaintext key material (`*.key`, `*.pem`) and
anything else under `secrets/` stays ignored. The pre-commit secret
scanner most consumers use is the second line of defense.

secrets/README.md documents the workflow at the directory level.
The substantive beginner walkthrough lands in a follow-up commit at
docs/secrets-management.md.

`nix flake check --no-build` stays green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:44:55 +02:00
8ebf16d069 docs(readme): promote Claude orientation to a top-level section
The seeding mechanism was previously buried as a sub-subsection inside
Development flow. Promote it to its own ## Claude orientation section
with a concrete value-prop: a Claude session in
~/dev/lnbits/extensions/<ext>/templates/ inherits the Vue/Quasar UMD
"no self-closing tags" rule from the per-project CLAUDE.md, so the
model writes the explicit-close form instead of the silently-broken
self-close that would corrupt LNbits template nesting.

Lists the other gotchas the seeding surfaces (settings precedence,
auth decorators, upstream PR target, CLINK ↔ LNbits scope) so a
reader scanning the README sees what they get without enabling the
option first.

Development flow's day-to-day subsection now points down to the new
section instead of explaining the wiring inline; the wiring snippet
moved to the Claude orientation section where it sits next to the
value-prop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:38:52 +02:00
1a75a04c2f docs: add lnbits frontend gotchas + extension-dev reference
Two new docs to round out the lnbits-specific reference set:

docs/lnbits-frontend-gotchas.md — the Vue/Quasar UMD traps that don't
manifest under the build-step model most tutorials assume. Sections:

- No self-closing tags. UMD-loaded templates are parsed by the
  browser's HTML parser, which doesn't honor self-close on non-void
  elements; nesting silently breaks. Fine in .vue SFCs because the
  build step rewrites them — but lnbits templates aren't SFCs.
- CSS specificity vs Quasar utilities. LNbits applies !important
  overrides on .text-caption / .text-grey-*; class-based rules in
  extension pages lose. Reach for inline :style bindings or static
  style="" attrs instead.
- Cache busting via ?v={server_startup_time}. Bumping JS requires a
  server restart; templates re-render every request.
- Dark-mode color discipline. bg-{color}-1 utilities don't get a
  default text-inversion in dark theme; pair every pale background
  with an explicit text class.

docs/lnbits-extension-dev.md — auth + testing + migration pattern.
Sections:

- Auth decorators table: require_invoice_key, require_admin_key,
  check_admin, check_super_user. The require_admin_key vs
  check_admin distinction is the most common misuse — one is
  wallet-level write access (any user), the other is instance admin.
- Testing: FakeWallet for CRUD/API/UI, regtest only for real
  Lightning behavior. Why the dev CLI defaults to --fakewallet.
- The migrations_fork.py pattern: keep migrations.py byte-identical
  to upstream, put fork-only schema deltas in a sibling file loaded
  under <ext>_fork in dbversions. Covers: architecture facts
  (dbversions in core DB, no cross-DB atomicity → idempotent
  migrations mandatory), squash recipe for adoption, one-time
  dbversions surgery for installs that previously ran old fork
  migrations, the upstream-overlap rebase scenario.

Both linked from README "Further reading". No personal identity in
either file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 00:57:08 +02:00
0a5713c704 feat(claude): seed curated CLAUDE.md files into ~/dev/ workspace
Adds the omnixy-pattern out-of-store-symlink wiring so consumers can
opt into Claude Code orientation without copying anything by hand.
Files live in lnbits-sensei (so they evolve via PR), symlinks point
back at the consumer's checkout (so edits take effect on the next
Claude session without a nixos-rebuild).

New files under files/:

- dev-CLAUDE.md — generic workspace orientation. Workspace layout,
  quick command set, pointers to docs/. Opt-in (clobbers an existing
  ~/dev/CLAUDE.md, so off by default).
- lnbits-CLAUDE.md — per-project orientation for the lnbits worktree
  subtree. Inline summary of the four most-stepped-on gotchas
  (settings precedence, Quasar UMD self-closing rule, auth-decorator
  distinctions, upstream PR target = `dev` not `main`) plus pointers
  to the docs/ for full reference.

New options under lnbits-sensei.devEnv:

- scaffoldPath — absolute path to the consumer's lnbits-sensei
  checkout. types.str (not types.path) intentionally: types.path
  would copy the file into the Nix store and defeat
  mkOutOfStoreSymlink. Required when any claude.* flag is on.
- claude.enable — seeds ~/dev/lnbits/CLAUDE.md.
- claude.workspaceOrientation — additionally seeds ~/dev/CLAUDE.md.

Wiring lives in home.nix (gated via `osConfig.lnbits-sensei.devEnv.*`)
rather than the dev-env NixOS module, since the file destinations are
under home-manager's purview and the home-manager scope is where
`mkOutOfStoreSymlink` is in scope.

`nix flake check` stays green — `optionalAttrs` is lazy, so
`scaffoldPath` isn't accessed when claude.{enable,workspaceOrientation}
are both false (their defaults).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 00:54:55 +02:00
35ef01fd70 docs(workspace-notes): add settings precedence (.env vs DB)
LNbits has two sources of truth for settings depending on lifecycle.
The trap: on first boot, .env seeds the DB; on every subsequent boot,
the DB row overwrites the in-memory Settings. Editing .env after the
first boot is a no-op for editable fields — they can only change via
the Admin UI or by clearing rows in system_settings.

Documented:
- the boot-time read-DB → seed-from-env → overwrite-cached-Settings
  sequence with file:line references for verification
- exceptions where env still wins (super_user, lnbits_admin_ui=False,
  the full ReadOnlySettings field list)
- LNBITS_FIRST_INSTALL_TOKEN rotation does NOT reset settings to env
  (it's an admin-recovery escape hatch, not a config-refresh)
- the deploy-side implication: declarative env vars are a *seed*
  for EditableSettings, authoritative for ReadOnlySettings — plan
  your deploy story accordingly

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 20:32:48 +02:00
6c9f76bd70 docs: surface lnbits workspace gotchas as a committed reference
New docs/lnbits-workspace-notes.md collects the day-to-day gotchas
that have repeatedly surprised people doing lnbits dev work — content
that lives in personal CLAUDE.md / memory notes elsewhere but is
genuinely useful as a public reference once stripped of identity.

Sections:
- pick a non-default port (5000 collides with macOS AirPlay etc.)
- LNBITS_SRC + docker-compose build context (commits don't reach
  the dev image if the build context points at a stale worktree;
  `docker compose config | grep -A2 lnbits` to verify)
- extension folder mounted as install target: clicking "Upgrade"
  in the LNbits UI extracts the catalog tarball over the mounted
  fork checkout, wiping .git
- Nostr key handling: upstream stores user nsecs plaintext in
  accounts.prvkey; signer-abstraction shape (LocalSigner with
  envelope-encrypted blob / RemoteBunkerSigner via NIP-46 /
  ClientSideOnlySigner) with the "prefer don't-store-the-key"
  principle
- CLINK protocol scope: Shocknet's 21001-21003 event kinds are
  Lightning.Pub-specific; LNbits has zero CLINK knowledge; how
  to think about cross-system designs without conflating them
- fork versioning: v<upstream>-<tag>.<N> + the PEP 440
  `+<tag>.<N>` package-version form for pyproject.toml
- codebase reading tips (key paths, --first-parent log reading)

Linked from README "Further reading". No personal identity in the
new doc — scrubbed of aiolabs/atitlan/bohm/etc references.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 20:24:28 +02:00
30fbd0cef9 docs: add upstream-PR workflow + lnbits branch-model reference
Two new docs under docs/:

- upstream-prs.md — how to send a PR upstream using the
  ~/dev/upstream-prs/ worktree flow. Opens with a "Primer" section
  for anyone new to forks / pull requests (the three remote roles —
  upstream / github-fork / origin — explained as a table), then the
  per-project prerequisites, the prb/prc helper-driven workflow, and
  a "Common pitfalls" section.

- lnbits-upstream-flow.md — reference for how lnbits/lnbits actually
  moves: the dev/main branch split, the squash-merge PR convention,
  the non-FF release merge from dev into main, and how to read the
  history with --first-parent. Adapted from internal aiolabs notes
  with the fork-versioning specifics stripped; closing section is
  generic guidance for anyone maintaining their own long-lived fork.

README links both from a new "Further reading" section. The prior
"Contributing" section is retitled "Contributing to this scaffold"
to avoid colliding with the new upstream-PR doc on the term.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 20:07:03 +02:00
3f88551f1b docs: showcase the ~/dev/ workspace layout the dev-env materializes
The previous "Project worktrees (planned)" subsection only described
one project's worktrees in the abstract. Replace with a section that
shows the full ~/dev/ tree the dev-env module is designed to build —
bare repos under `~/dev/repos/`, per-project worktree dirs, and a
separate `~/dev/upstream-prs/` tree for PRs branched from upstream's
main.

Includes the rationale (one object DB, no clone churn; explicit PR
contract via the separated tree) and stub examples of the navigation
helpers (`lb dev`, `prb lnbits foo`) the module will eventually
generate from `devEnv.projects`.

Renames the prior "## Layout" → "## Repository layout" so the README
distinguishes the two trees (this repo's source layout vs the
on-disk workspace it manifests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:37:16 +02:00
6959d13120 docs: showcase folder layout + bootstrap/dev/worktree flow
Adds two sections to the README:

- `## Layout` — annotated tree of the actual files in the repo, with
  `(stub)` markers on the modules whose bodies are TODO. Replaces the
  previous "Planned shape" bullet list (was aspirational and lying about
  files that didn't exist, e.g. `modules/lnbits/`).
- `## Development flow` — three subsections: first-time bootstrap (clone,
  settings, nixos-generate-config, flake check, switch), day-to-day via
  the `dev` CLI, and project worktrees from `devEnv.projects`.

Status line bumped from "scaffold in progress" to "eval-green skeleton"
to match where we actually are — module schema + flake wiring are real,
CLI + dev-env bootstrap remain stubs.

Renames `## Development` to `## Contributing` since "Development flow"
is now the user-facing section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:32:39 +02:00
45c44f550e chore: get nix flake check evaluation green
Three blockers off the path so the skeleton evaluates cleanly:

1. flake.lock generated (no inputs were pinned before).
2. hardware-configuration.nix shipped as a placeholder — unbootable
   /dev/null root + grub on nodev — so `system.build.toplevel` resolves
   without complaining about missing fileSystems / boot.loader. The
   file carries a big "overwrite with nixos-generate-config" warning;
   the consumer regenerates it before any real switch.
3. Primary user + group declared in configuration.nix (was tripping
   the assertion about implicit nogroup defaults and isNormalUser
   missing).

Also: `home.homeDirectory` now uses `lib.mkForce` to override
home-manager's nixos-module default of /var/empty, and the deprecated
`programs.git.user{Name,Email}` options migrated to the new
`programs.git.settings.user.{name,email}` shape.

`nix flake check --no-build` now finishes with `all checks passed!`
and no warnings, giving every subsequent change a cheap signal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:12:11 +02:00
6cabca3113 docs: rewrite README around the dev CLI + remotes
Reflects the post-cleanup shape: single `dev up [--fakewallet|--regtest]`
entry point (the two parallel wrappers are gone), no modules/lnbits/
line (deleted), license footer points at the MIT file we already shipped,
and a settings.nix.git.remotes snippet so a reader sees the topology
toggle without opening docs/remotes.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 13:09:46 +02:00
94a7c5f97c refactor: collapse fakewallet/regtest wrappers into single dev CLI
Replaces the two parallel scripts (fakewallet.sh, regtest.sh) with one
modules/dev-env/scripts/dev.sh — `dev up [--fakewallet|--regtest]`,
plus `down|logs|shell`. Default mode is fakewallet (no docker, no
chains, instant), matching what the prior scaffold did with two scripts
but giving consumers one command and one verb-set to learn.

Drops the now-redundant `lnbits.backend` enum and `features.fakewallet`
option from core.nix. Backend selection is the dev CLI's runtime
concern; a NixOS-level option would be a second knob that can disagree
with the CLI flag at runtime. `lnbits.{host,port}` stay (bind addr,
useful to docs and any later service path). `features.regtest` stays
(gates docker engine installation — consumers who'll never use the
regtest mode shouldn't pay for the container engine).

Strips modules/lnbits.nix entirely. The dev CLI runs lnbits ad-hoc; if
a NixOS-managed lnbits service becomes a real ask later, re-add a
focused module then.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:56:05 +02:00
bbaa80d8a3 chore: add MIT LICENSE with placeholder year + holder
Consumer fills in <year> and <copyright holder> when forking. Leaving
placeholders rather than baking in an identity keeps the template
neutral.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 23:08:53 +02:00
c4bf077636 chore: stub regtest.sh + fakewallet.sh dispatchers
Both scripts are placeholders that document the intended dispatcher
shape (up/down/logs/shell). fakewallet.sh is a deliberate no-op for
symmetry — same muscle memory whether the backend is a docker stack
or a constant.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 23:03:11 +02:00
a21428e482 chore: scaffold dev-env module (options + config + lib stubs)
default.nix composes the three sub-modules. options.nix declares the
public surface (projects, regtest, fakewallet, tmux) so consumers can
wire values today even though config.nix is empty. lib.nix reserves
dev-env-scoped helpers separate from the global lnbits-sensei.lib.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 23:01:37 +02:00
6adc82e8f3 chore: add git remote topology module + docs
modules/git/remotes.nix declares upstream/fork/extras schema. extras is
a typed submodule list so order is preserved and future fields
(pushUrl, mirror) can extend without breaking callers. docs/remotes.md
walks the three canonical topologies (upstream-only / github-fork /
multi-remote with private host).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 22:36:33 +02:00
8cc59b3024 chore: stub option schema + lib + lnbits service wrapper
core.nix defines the lnbits-sensei.* options (user, host, features,
backend). lib.nix reserves the config.lnbits-sensei.lib helper namespace
with placeholder stubs. lnbits.nix is a no-op stub that documents the
intended services.lnbits wrap shape and the FakeWallet default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 22:34:53 +02:00
3f528623b3 chore: scaffold flake + settings + entry-point quartet
Single-source-of-truth pattern: settings.nix threads identity, host,
and remote topology into every module via specialArgs. configuration.nix
and home.nix stay thin import-lists so module composition is obvious.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 22:25:52 +02:00
677b8c7c27 chore: scaffold repo skeleton
Empty placeholder commit anchoring the repo. Substantive scaffolding
(flake.nix, modules/, settings.nix) lands in follow-ups iterated under
the sandboxed-claude policy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 19:21:12 +02:00