Commit graph

294 commits

Author SHA1 Message Date
fabro-releases[bot]
7fd8f5a57b Bump version to 0.212.0-nightly.0 2026-04-23 09:45:03 +00:00
Bryan Helmkamp
28a9f036d8
fix: server-side settings authority + unit tests that never hit live S3
Two related correctness bugs surfaced by the failing test suite:

1. Server-owned settings didn't flow into run settings, and the few
   server-only fields that did leak in made run snapshots bulky and let
   callers re-resolve server state from the run layer.

   - effective_settings::materialize_settings_layer now treats the
     server's run/features stanzas as base defaults (client layers
     still win where set), and enforce_server_authority keeps the
     original cherry-pick of storage/scheduler/artifacts/web/api but
     no longer lets the rest of the server namespace propagate. auth,
     listen, ip_allowlist, slatedb, logging, and integrations stay on
     the server, where AppState::server_settings() already has them.

   - run_preflight, the scheduler start-path, and operations::start
     now read GitHub integrations from state.server_settings() (or
     StartServices::github_permissions, which the server populates)
     instead of re-resolving the server namespace from the run's
     settings layer.

   - create_app_state{_with_options,_with_env_lookup,_with_options_and_registry_factory}
     and create_app_state_with_store_and_env_lookup all route through
     ensure_test_auth_methods so the strict resolver accepts
     SettingsLayer::default() in tests.

   - Fixed the start_run_persists_full_settings_snapshot assertion
     that expected server.integrations.github.app_id in the run's
     persisted settings — the new design deliberately omits it.

2. Unit and integration tests were hitting live AWS S3.

   - Added a NoProxyReqwestConnector (behind a dedicated reqwest 0.12
     dep aliased as object_store_reqwest) and wired it through
     AmazonS3Builder::with_http_connector. macOS SystemConfiguration
     proxy discovery in the default reqwest client was blowing past
     nextest's 20s kill timeout on serve.rs's S3 builder unit tests;
     the no-proxy connector brings them under 15ms.

   - InstallAppState::for_test_with_paths now sets
     FABRO_TEST_IN_MEMORY_STORE=1 so /install/finish's artifact-metadata
     sentinel write short-circuits to the in-memory object store and
     never contacts AWS. The install integration tests verify
     persistence/redaction, not S3 reachability.

`cargo nextest run --workspace`: 4495/4495 passing.
`cargo +nightly-2026-04-14 fmt --check --all`: clean.
`cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings`: clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 23:15:56 -04:00
Bryan Helmkamp
fa62da5d9d
Merge remote-tracking branch 'origin/main'
# Conflicts:
#	lib/crates/fabro-api/build.rs
#	lib/crates/fabro-server/src/install.rs
#	lib/crates/fabro-server/src/serve.rs
#	lib/packages/fabro-api-client/src/.openapi-generator/FILES
#	lib/packages/fabro-api-client/src/models/index.ts
2026-04-22 21:18:50 -04:00
Bryan Helmkamp
ef78228c06
refactor(install): flatten resolver, dedupe fetch wrappers, share step-submit helper
- Derive strum::IntoStaticStr on InstallObjectStoreProvider/CredentialMode and use it in as_session_value instead of a hand-written match.
- Split resolve_install_object_store_state: extract resolve_s3_manual_credentials and fold the redundant outer "missing credentials" guard into its (None, None) arm.
- Replace the per-endpoint installFetch boilerplate with installRequest / installJsonRequest<T> so each install-api wrapper is a single call.
- Extract runStepSubmit inside InstallApp; the LLM, server, object-store, and GitHub step handlers now share the setSubmitting / try / refresh-session / navigate / finally scaffolding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 20:21:13 -04:00
Bryan Helmkamp
4bf0c40319
feat(install): add object-store step to web install 2026-04-22 19:31:09 -04:00
Bryan Helmkamp
ebb8bf7add
refactor settings API entrypoints 2026-04-22 18:58:47 -04:00
Bryan Helmkamp
18cfd1b92d
refactor: adopt strum for enum string conversions
Replace hand-written Display/FromStr/as_str boilerplate with strum
derives on Provider, RunStatus, StatusReason, Speed, ReasoningEffort,
SandboxProvider, Fidelity, ModelTestMode, ModelTestStatus. Update a few
downstream callers whose FromStr::Err = String assumption no longer
holds. Net -172 lines, zero wire-format change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:16:25 -04:00
Bryan Helmkamp
911f957200
refactor(config): move Bind and ServerDaemon into fabro-config
`Bind` and `ServerDaemon` are serde-serialized descriptions of on-disk
server state (the `server.json` record). They belong with
`RuntimeDirectory` in fabro-config rather than in fabro-server's web
layer.

The practical payoff: fabro-test was hand-parsing `server.json` via
`serde_json::Value["pid"]` because fabro-server already depends on
fabro-test (cycle blocked the reverse edge). Moving these types into
fabro-config lets fabro-test call `ServerDaemon::{load_running, read,
remove}` directly, dropping ~20 lines of duplicated record parsing.

fabro-config gains `fabro-proc` and `tempfile` as deps to cover
`ServerDaemon::{is_running, write}`. All 16 `fabro_server::{bind,
daemon}` import sites in fabro-server and fabro-cli are rewritten to
`fabro_config::{bind, daemon}`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:44:59 -04:00
fabro-releases[bot]
090e1022ed Bump version to 0.211.0-nightly.1 2026-04-22 15:12:04 +00:00
Bryan Helmkamp
7cee33d06b
Merge remote-tracking branch 'origin/main' 2026-04-22 07:50:55 -04:00
Bryan Helmkamp
0781e3a2c1
test(cli): suppress browser launch under spawned fabro test subprocesses
Add FABRO_SUPPRESS_OPEN_BROWSER env knob via fabro_util::browser::try_open.
apply_test_isolation now sets it, so install-mode and auth-login tests that
spawn a real fabro binary no longer pop real browser windows. All six
open::that call sites route through the helper; consolidates the direct
open crate dep into fabro-util.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 07:50:34 -04:00
fabro-releases[bot]
a79c7155fd Bump version to 0.211.0-nightly.0 2026-04-22 09:43:45 +00:00
Bryan Helmkamp
105559bc8a
feat(cli): exit with code 4 on authentication-required failures
Returns exit code 4 whenever the CLI fails because the user needs to run
fabro auth login, so scripts and the install wizard can distinguish
re-auth from generic failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 23:34:21 -04:00
fabro-releases[bot]
f55b0db427 Bump version to 0.210.0-nightly.0 2026-04-21 19:19:51 +00:00
Bryan Helmkamp
6eee5a5a72
Merge remote-tracking branch 'origin/main' 2026-04-21 09:44:10 -04:00
Bryan Helmkamp
eb7e65b6b4
refactor(api): reuse domain status types in fabro-api
Replace the generated status family with fabro-types replacements and add
parity tests that lock type identity and JSON shape to the OpenAPI spec.
2026-04-21 09:10:53 -04:00
Bryan Helmkamp
4865efa499
refactor(store): simplify Repository and blob wiring
- Use hex crate for [u8; 32] RecordId instead of hand-rolled loops
- Drop dead prefix_segments cache field; key assembly consumes
  R::PREFIX.split('/') directly, removing an intermediate Vec<&str>
- Cache BlobStore on RunDatabaseInner (built once in open_writer/
  open_reader via a new build() helper) instead of per-blob construction
- Trim the replay_revocations doc comment to drop a stale plan reference

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 09:00:00 -04:00
Bryan Helmkamp
0d2be41e57
Merge remote-tracking branch 'origin/main' 2026-04-21 08:38:37 -04:00
Bryan Helmkamp
bf1aa6eed5
Merge remote-tracking branch 'origin/main'
# Conflicts:
#	lib/crates/fabro-store/src/lib.rs
#	lib/crates/fabro-store/src/slate/mod.rs
#	lib/crates/fabro-store/src/slate/run_store.rs
2026-04-21 08:35:07 -04:00
Bryan Helmkamp
f75e5c2ef6
refactor(store): extract Record/Repository abstractions
Replaces hand-written K/V stores in fabro-store with a shared Record trait
plus Repository<R> typed K/V layer. Adds KeyedMutex for per-key serialization
and transaction() for all-or-nothing WriteBatch commits. Renames
SlateAuthCodeStore/SlateAuthTokenStore to AuthCodeStore/RefreshTokenStore and
adds BlobStore and RunCatalogIndex wrappers on top of Repository. Deletes
catalog.rs in favor of RunCatalogIndex. Database gains blobs() and
catalog_index() accessors; auth_tokens() is renamed refresh_tokens().

Plan: docs/plans/2026-04-20-003-refactor-fabro-store-record-abstractions-plan.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 08:29:34 -04:00
Bryan Helmkamp
705dccdea7
Merge remote-tracking branch 'origin/main'
# Conflicts:
#	lib/crates/fabro-store/src/lib.rs
#	lib/crates/fabro-store/src/run_state.rs
2026-04-21 07:53:10 -04:00
Bryan Helmkamp
7dd058cc40
refactor: unify run vocabulary and metadata snapshot layout
Implements the plan at
docs/plans/2026-04-20-003-refactor-unify-run-vocabulary-metadata-plan.md.

- Rename RunRecord to RunSpec and RunProjection.run to .spec everywhere
  in Rust source, tests, helpers, test names, and error messages.
- Introduce SerializableProjection wrapper that trims bulky node text
  fields (prompt, response, diff, stdout, stderr) for run.json snapshots.
- Collapse metadata-branch and CLI export to one RunDump::from_projection
  builder emitting run.json + graph.fabro + stages/{stage_id}/... and
  drop legacy top-level start/status/checkpoint/sandbox/retro/conclusion
  split files.
- Replace MetadataStore::write_checkpoint with write_snapshot returning
  the commit SHA; add read_run_projection/read_run_spec; demote
  read_checkpoint/read_start_record to projection-field extractors.
- Switch fork, rewind, rebuild_meta, CLI rewind recovery, and retro
  upload to read the unified projection layout.
- Add additive query methods on RunSpec and RunProjection.

Serde-level `alias = "spec"` shim dropped; `rename = "run"` retained to
keep the server API wire format stable per the plan's scope boundary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:17:23 -04:00
Bryan Helmkamp
f0f04abf44
refactor(client): extract fabro-client crate
Lift shared client DTOs into fabro-types, move auth/target/error/session
logic into fabro-client, and reduce fabro-cli to orchestration around the
builder-based client path.

This also lands the remaining plan cleanup for ApiError, ServerTarget
canonicalization, and the RunEventStream rename at the CLI boundary.
2026-04-20 20:42:20 -04:00
Bryan Helmkamp
acd89fb235
refactor: remove CLI client mTLS
Delete the CliTargetTls settings, ClientTlsSettings struct, rustls-pemfile
dep, and the fabro-http wrapper methods (use_rustls_tls/identity/
add_root_certificate) that only the CLI client-auth path used. The server
no longer terminates TLS in-process and the CliAuthStrategy::Mtls variant
had no construction or match sites.

Also simplify ServerTarget::HttpUrl to a tuple variant (HttpUrl(String))
now that tls is gone, removing the struct-variant ceremony across 22
construction and destructure sites.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 17:37:24 -04:00
Bryan Helmkamp
4c35c4b69f
Merge remote-tracking branch 'origin/main'
# Conflicts:
#	lib/crates/fabro-server/src/jwt_auth.rs
#	lib/crates/fabro-server/src/serve.rs
#	lib/crates/fabro-server/src/server.rs
#	lib/crates/fabro-server/src/web_auth.rs
2026-04-20 09:47:47 -04:00
Bryan Helmkamp
153fcf20b5
fix(auth): require browser confirmation for CLI login
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.
2026-04-20 09:40:05 -04:00
Bryan Helmkamp
6ac8bf6d81
fix(auth): harden CLI auth review gaps
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.
2026-04-20 09:03:22 -04:00
Bryan Helmkamp
9c3c66c59a
test(http): improve HTTP test failure diagnostics
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.
2026-04-20 08:06:14 -04:00
Bryan Helmkamp
f2a512185e
test(auth): add remaining CLI auth integration coverage 2026-04-20 08:05:22 -04:00
Bryan Helmkamp
9c68c57bcc
feat(auth): add CLI GitHub login and logout flow
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.
2026-04-20 07:12:52 -04:00
Bryan Helmkamp
4b3835b8ee
test(server): cover resolve_head_sha_and_time parser + two-phase fetch
Two coverage gaps closed:

1. `parse_head_show_output` — extracted from `resolve_head_sha_and_time`
   as a pure function so it can be tested without a sandbox. Six tests
   cover: well-formed sha+iso line, non-UTC timezone normalization,
   sha-only output (missing %cI), malformed date (parser tolerates
   and returns sha with None date), empty-input rejection, and
   surrounding-whitespace tolerance. New code from the simplify pass,
   previously unverified.

2. `fetch_blob_table` two-phase error isolation — `ScriptedBlobSandbox`
   (hand-written minimal Sandbox impl) returns different exec responses
   for `cat-file --batch-check` vs `cat-file --batch`. The phase-2
   failure test proves that a malformed --batch parse outcome doesn't
   corrupt phase-1-classified oversized entries — the doc-comment's
   promise that the two phases are isolated now has a regression test
   behind it. The phase-1-skip test enforces the
   METADATA_PHASE_SHA_THRESHOLD contract by making phase 1's
   batch-check response an error: if the threshold logic regressed
   and phase 1 ran, the test would fail with a 503.

Also adds `Debug` to `ApiError` (required by `Result::expect` in the
new tests) and adds `async-trait`/`tokio-util` as dev-dependencies
plus the `test-support` feature on fabro-sandbox.

Total workspace test count: 4173 -> 4180.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 19:21:19 -04:00
Bryan Helmkamp
0c77a184ab
fix(web,server): pierre 1.1 API fit + unicode-safe path normalize
Three follow-ups from verification against the actual @pierre/diffs
1.1.15 type definitions:

1. Deep-link expand uses `options.expandUnchanged: true` on the
   targeted MultiFileDiff rather than firing `el.click()` on the outer
   wrapper. Pierre 1.1.x exposes no imperative expand API — click on
   the row container was a no-op. Per-file expansion now fires on
   mount when the file name matches the URL hash.

2. Enter/Space binding removed from useFileKeyboardNav — click on the
   outer row doesn't trigger anything in pierre's model, and binding
   it just delayed default browser scroll behavior on Space. j/k
   focus navigation remains the working keyboard affordance. When a
   pierre imperative expand API appears, Enter/Space can be re-added
   to call it.

3. normalize_for_match strip loop now iterates to a fixed point
   against the fully-lowercased string so repeated `./` / `../` / `/`
   prefixes are all stripped. Added Windows-path and Unicode-uppercase
   regression tests for is_sensitive to verify basename matching
   survives both.

Virtualizer usage verified against the 1.1.x type definitions: the
`{ children: ReactNode }` signature accepts the wrapped file list
directly with no Virtualizer.Item wrapper needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 17:55:20 -04:00
Bryan Helmkamp
1ef72fb6bc
refactor(server): extract run_files_security with globset denylist (P3-2)
Moves the sensitive-path denylist, sandbox-git env helper, and metrics
emitter into a dedicated run_files_security module so the Run Files
Changed endpoint has a single, testable surface for security controls.

Denylist upgrades to globset::GlobSet with two explicit lists:
- Basename globs: .env, .env.*, *.pem, id_rsa, id_rsa.*, id_ed25519*,
  *.p12, *.keystore, *.key
- Path-suffix globs: .aws/credentials, .git/config, .ssh/**

Matching semantics explicitly pinned:
- Case-insensitive via lowercased normalization
- Path traversal (`../`, `./`, leading `/`) stripped before match
- Basename globs match the final segment only — prevents
  `log/.env_audit/data.txt` from matching `.env.*`
- Empty/pathological paths fail closed (sensitive=true safe default)

Also ships:
- sandbox_git_env() returning the env-hardening map
- RunFilesMetrics struct + emit() so tracing never leaks paths/contents

Handler migrates to consume the new module; inline denylist and inline
info!() call removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 17:35:26 -04:00
Bryan Helmkamp
c6ff36d9fb
refactor(install): consolidate shared primitives in fabro-install crate
The `fabro-install` crate was introduced for the web wizard but the CLI
kept its own copies of the same JWT keypair generation, TOML merging,
and GitHub auth settings helpers. Delete the duplicates and route the
CLI through `fabro_install::*`. The CLI keeps a thin
`merge_server_settings` wrapper because it only ever binds TCP and
derives the authority from `--web-url`.

Also tighten `persist_install_outputs_direct` to take its
`PendingSettingsWrite` argument by reference (satisfies
`needless_pass_by_value`) and pull the remaining absolute paths in the
crate's test module into `use` statements, clearing the nightly clippy
warnings that this branch was carrying.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 13:54:47 -04:00
Bryan Helmkamp
985373cad4
chore(lockfile): sync merged workspace versions 2026-04-19 13:37:10 -04:00
Bryan Helmkamp
dd4e467bfc
Merge remote-tracking branch 'origin/main'
# Conflicts:
#	lib/crates/fabro-server/tests/it/api/mod.rs
2026-04-19 13:36:54 -04:00
Bryan Helmkamp
e9daf2db3a
Merge remote-tracking branch 'origin/main' 2026-04-19 11:48:15 -04:00
Bryan Helmkamp
ecdfdd82d8
feat(install): add browser-based setup flow
Implement the web-first install experience across the server, CLI, API spec,
web app, and packaged SPA assets.

This also removes test-side process env mutation by pushing env-dependent
decision points behind explicit helpers and test wiring.
2026-04-19 11:20:58 -04:00
Bryan Helmkamp
914778c8a8
refactor(server): remove inbound TLS termination
Remove server-side TLS listener support so Fabro only binds plain TCP
or Unix sockets, and update docs/tests around proxy-terminated HTTPS.
This also drops the removed [server.listen.tls] config shape and the
inbound TLS-specific diagnostics, fixtures, and integration coverage.
2026-04-19 10:43:57 -04:00
fabro-releases[bot]
51faecf266 Bump version to 0.208.0-nightly.1 2026-04-19 14:27:05 +00:00
Bryan Helmkamp
1e03216161
feat(server): add IP allowlist middleware with GitHub webhook support
Introduces a configurable IP allowlist applied to the main API router
and the GitHub webhook listener. Supports CIDR literals plus a
`github_meta_hooks` keyword that resolves live against GitHub's meta
API for the webhooks override. Adds trusted-proxy handling for
X-Forwarded-For, validation that rejects Unix socket listeners without
a trusted proxy count, and deep-merge logic for the new
server.ip_allowlist and per-integration override layers.
2026-04-19 09:47:22 -04:00
fabro-releases[bot]
7933e1ed5c Bump version to 0.208.0-nightly.0
Some checks are pending
Rust / Format (push) Waiting to run
Rust / Clippy (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
TypeScript / Test (push) Waiting to run
TypeScript / Build (push) Waiting to run
TypeScript / Typecheck (push) Waiting to run
2026-04-19 00:27:15 +00:00
fabro-releases[bot]
769f078bc8 Bump version to 0.207.0-nightly.6 2026-04-18 21:15:59 +00:00
fabro-releases[bot]
6ebe609e71 Bump version to 0.207.0-nightly.5 2026-04-18 17:26:06 +00:00
Bryan Helmkamp
977f4b4a77
deps: bump rand 0.9.2 -> 0.9.4
Patches GHSA-cq8v-f236-94qc (RUSTSEC-2026-0097) for direct rand usage.
The transitive rand 0.8.x remains in the lockfile via cookie, sentry,
slatedb, and phf_generator pending upstream bumps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:16:59 -04:00
fabro-releases[bot]
d066592362 Bump version to 0.207.0-nightly.4 2026-04-18 16:47:30 +00:00
fabro-releases[bot]
0bd72d0b2d Bump version to 0.207.0-nightly.3 2026-04-18 16:24:51 +00:00
fabro-releases[bot]
da0999b697 Bump version to 0.207.0-nightly.2 2026-04-18 15:05:58 +00:00
fabro-releases[bot]
459433baa5 Bump version to 0.207.0-nightly.1 2026-04-18 10:05:08 +00:00
Bryan Helmkamp
70d6f929d2
chore: update Cargo.lock for vendored openssl dep
Records openssl 0.10 + openssl-src 300.6.0 in the lockfile for the
target-specific musl dep added in the previous commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 01:14:54 -04:00