Tighten CLI loopback target classification to use literal host checks,
update explicit local TCP auth coverage to match the remote-target
contract, and align server CSP assertions with the current external-script
SPA bundle. Also enable reqwest cookies in fabro-http so package-scoped
server tests compile without relying on workspace feature unification.
Consolidate three copies of `normalized_http_base_url` and
`build_public_http_client` into shared helpers in `user_config`,
add `Display for ServerTarget`, drop stale `#[allow(dead_code)]`
markers now that login/logout/JWT are wired, remove dead
`LOGIN_SUCCESSFUL` and `_error_description` field, and gate
test-only helpers behind `#[cfg(test)]`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Harden the CLI browser auth flow by moving auth-code issuance behind
an explicit same-origin confirmation step, and update the real-browser
test harness to submit the confirmation page.
Removes unused annotateRunningNodes from run-graph and inlines the
const gt = graphTheme / const theme = graphTheme shims left over from
the dark-mode-only refactor. Template strings reference graphTheme
directly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends handleLifecycleToastResult to cover the cancel intent and
switches cancel's effect onto the shared helper. lastProcessed is now
keyed per intent so the three effects don't clobber each other's dedup
state, and cancel picks up the same replay guard that archive and
unarchive already had.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds expect(disallowed_types) at the tests module for the intentional
sync BufReader usage in the zombie-process-group helper, and drops the
absolute-path call site by bringing pre_exec_setpgid into scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the light/dark toggle infrastructure in favor of a single dark
theme. Deletes the theme context, boot script, light-mode CSS overrides,
logotype-light asset, and the pierre-light diff theme. Collapses
graph-theme into a single constant. Adds scheme-only-dark on <html> so
native controls and the server-injected Graphviz @media query render
dark.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merges handleArchiveToastResult and handleUnarchiveToastResult into a
single helper. Replaces the content-hash dedup key with object identity
on fetcher.data and collapses the two "last key" fields into one
lastProcessed. Tests now import the exported helper directly instead of
casting through Record<string, unknown>.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Split raw PID existence from actual process liveness in fabro-proc and
switch the server shutdown paths to the running-process predicate. This
avoids waiting out stop timeouts for unreaped zombie children while
keeping process-group behavior covered by measured regression tests.
Add the missing refresh transport guard, actionable auth-store lock errors
for unsupported filesystems, explicit OAuth state expiry, and the remaining
CLI auth regression coverage around replay revocation, HTML headers, and
secret-safe logging.
Expose cancel, archive, and unarchive from the run detail view,
surface blocked-question context, and route run-detail and run-files
notifications through a single shared toast provider.
This also refreshes the embedded SPA bundle and marks the lifecycle
actions plan complete.
- Move the GitHub App webhook config update to fabro-github as
update_app_webhook_config, matching the crate's existing HttpClient +
Result<_, String> conventions. Server-side callers go through the new
symbol.
- Add Bind::tcp_port() on the enum itself and drop the free function.
- Collapse the six near-identical "webhook strategy configured but ...;
skipping webhook startup" warn branches into resolve_webhook_preconditions
returning a Ready/Skip enum, with one warn! at the call site.
- Replace the per-file test-helper wrappers (assert_status, checked_response,
response_json, response_bytes) with local macro_rules! macros so
file!()/line!() expand at the caller. Panic context now identifies the
failing assertion's source line instead of the wrapper's definition.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Distinguish the two GitHub webhook auth-failure warn messages (missing
signature header vs. HMAC mismatch) so logs can tell them apart.
- Route update_github_app_webhook through fabro_github::github_api_base_url()
so GITHUB_BASE_URL overrides the webhook config endpoint too.
- Drop a narrative shutdown comment that restated the next two lines.
- Replace concat!(file!(), ":", line!()) inside local test-helper wrappers;
those macros expand at the wrapper definition site, so every panic
reported the same phantom location. Pass the wrapper name instead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SHA-256 is already a workspace dep and is what the implementation uses at
server_client.rs:792-795. Avoids adding blake3 as a new dep for a use where
the algorithms are equivalent.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the real CLI auth integration harness into shared test support so
scenario/auth.rs keeps only the scenario cases and mock-browser helpers.
This makes the real-server auth setup reusable for future CLI integration
tests without duplicating the bootstrap code.
Add shared axum/reqwest response assertion helpers in fabro-test,
migrate the Rust HTTP test surface to use them, and document the
new rule in the testing strategy.
CI's TypeScript Build job upgraded bun to 1.3.13 (via setup-bun@v2.2.0
pulling the latest release), which produces a different content-hashed
entry CSS than the bundle committed under bun 1.3.10. The drift was
caught by the widened path filter in c8b807f30 and failed the
git diff --exit-code check on lib/crates/fabro-spa/assets.
Rebuilds with bun 1.3.13 so the embedded SPA matches the build CI
reproduces.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Retry CLI API calls once after a 401 access_token_expired response by
refreshing the stored OAuth session and rebuilding the generated API
client. Clear local auth when the refresh chain is expired or revoked,
and add end-to-end CLI scenarios covering login, authenticated use,
refresh, and logout.
- Capture webhook secret at route mount time via Arc<[u8]> router
state so the handler drops its per-request server_secret lookup
and the dead NOT_FOUND fallback.
- Extract WEBHOOK_ROUTE and WEBHOOK_SECRET_ENV constants; apply
across serve.rs, server.rs, and TailscaleFunnelManager so the
mounted route and the URLs pushed to GitHub cannot drift.
- Flatten the seven-level nested webhook startup match in serve.rs
into a single start_webhook_strategy helper with early returns,
short-circuiting when the secret is absent and replacing the
server.api.url .expect with a propagated error.
- Share compute_signature and a new read_repo_file helper across
tests; delete the duplicated webhook_signature, TestHmacSha256,
and read_doc/repo_root copies.
- Replace the nested for-loops in the new webhook auth tests with
five flat #[tokio::test] cases per CLAUDE.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prevents silent bun.lock drift across CI runs (root cause of the
nightly fabro-spa staleness failure) and re-runs typescript.yml when
nightly.yml itself changes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Model the GitHub webhook request body as JSON so the generated TypeScript
client exposes a coherent request shape, and add explicit conformance
coverage for the secret-gated webhook route.
Add the server-side CLI OAuth endpoints and token persistence needed to
mint JWT access tokens and rotating refresh tokens from the existing
GitHub web auth flow.
Add CLI auth storage plus `fabro auth login`, `logout`, and `status`, and
prefer stored OAuth access tokens when building target clients.
Move GitHub webhook intake onto the main API router, add explicit
server_url and tailscale_funnel strategies, and validate strategy
requirements at config resolution. This also updates the API contract,
generated client, and operator docs to match the new webhook model.
Enable clippy's unwrap_used lint at warn level, document the long-term
policy carveouts for tests and LockResult, and localize the generated
OpenAPI client exemption so the remaining warning surface is real repo
code.
Enable clippy::allow_attributes_without_reason at the workspace level.
Add concise, callsite-specific reasons to existing allow attributes, including generated code paths.
Resolve rm/archive/unarchive selectors through the server-owned
runs/resolve endpoint instead of CLI-side summary matching, and move
active-run delete force semantics into DELETE /runs/{id}.
Reverts the `single_node_ack` acknowledgment flag and all the cross-node /
multi-node qualifier text introduced in the previous two commits. fabro-server
is single-node by design; there is no multi-node deployment model to design
against. The in-process per-hash and per-code mutexes in Units 9 and 10
provide the full atomicity guarantees R3 requires. R12 restored to the
original web-enabled + SESSION_SECRET-length check. No ack flag, no
config-surface EULA, no cross-node tests, no multi-node risks-table row.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds `server.auth.github.single_node_ack: bool` (default `false`) as a new
config field required by startup validation when `github` is in
`auth.methods`. Without the flag, the server refuses to boot. This turns the
previously documentation-only single-node constraint into a fail-closed
startup check — an operator can still misdeploy to multi-node after setting
the flag, but they must affirmatively acknowledge the tradeoff first.
Alternative (auto-detect via SlateDB boot-heartbeat) deferred as future
work; explicit acknowledgment is lower-complexity and avoids rolling-deploy
false positives. Enforcement lives in Unit 5 startup validation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a 22-unit implementation plan for `fabro auth login`/`logout`/`status`
with GitHub OAuth + PKCE, HS256 JWT access tokens, rotating opaque refresh
tokens, and a typed `IdpIdentity` flowing across fabro-types / fabro-store /
fabro-server. v1 is scoped to single-node deployments with `github` in
`auth.methods` (distributed refresh-token rotation coordination is deferred).
Origin spec: docs/superpowers/specs/2026-04-19-cli-auth-login-design.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two follow-ups the workspace lint now catches:
- fabro-server tests/it/api/install.rs: a newer install-router integration
test was missing the `.await` after `build_install_router(...)` -- the
fn became async when the devcontainer/install-mode resolver was
converted to tokio::fs in commit 19939c5f0.
- fabro-cli main.rs: add #[expect(clippy::disallowed_methods)] to the
#[cfg(test)] module whose write_test_settings helper uses sync
std::fs::write to stage CLI settings fixtures.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>