diff --git a/run.json b/run.json index 220c469aa..21ea4d165 100644 --- a/run.json +++ b/run.json @@ -503,7 +503,7 @@ "kind": "running" }, "status_updated_at": "2026-05-28T00:13:58.936868Z", - "last_event_at": "2026-05-28T00:16:35.033591Z", + "last_event_at": "2026-05-28T00:52:56.713831Z", "pending_control": null, "checkpoints": [ { @@ -688,9 +688,9 @@ } }, { - "seq": 0, + "seq": 48, "checkpoint": { - "timestamp": "2026-05-28T00:18:58.022473Z", + "timestamp": "2026-05-28T00:19:01.907865Z", "current_node": "preflight_lint", "completed_nodes": [ "start", @@ -700,8 +700,108 @@ ], "node_retries": {}, "context_values": { + "internal.retry_count.preflight_lint": 0, + "internal.retry_count.start": 0, "thread.start.current_node": "toolchain", + "thread.toolchain.current_node": "preflight_compile", + "graph.rankdir": "LR", + "graph.goal": "---\ntitle: \"refactor: Remove IP allowlisting\"\ntype: refactor\nstatus: active\ndate: 2026-05-27\n---\n\n# refactor: Remove IP allowlisting\n\n## Overview\n\nRemove Fabro's inbound server IP allowlisting feature completely. This removes the\nserver-wide `[server.ip_allowlist]` setting, the GitHub webhook-specific\n`[server.integrations.github.webhooks.ip_allowlist]` overlay, request middleware,\nclient-IP extraction, GitHub `/meta` hook-range expansion, settings API exposure,\ngenerated API client types, and the Settings > Security UI display.\n\nExisting configs that still contain removed IP allowlist keys should fail as\nunknown fields. This is an intentional hard removal, not a compatibility\ndeprecation.\n\n## Problem Frame\n\nThe feature is being removed from Fabro rather than maintained as an in-process\nnetwork access-control layer. Network source restrictions should be handled\noutside Fabro by reverse proxies, firewalls, VPNs, Tailscale, platform ingress,\nor other deployment-layer controls.\n\n## Requirements Trace\n\n- R1. Remove all runtime enforcement of inbound source-IP allowlisting from web,\n API, static asset, and GitHub webhook routes.\n- R2. Remove the server settings schema for global and GitHub webhook IP\n allowlists.\n- R3. Remove `IpAllowEntry` and related API schema/client types from public\n settings payloads.\n- R4. Keep unrelated allowlists intact: GitHub username allowlists, sandbox\n egress CIDR allow lists, and internal policy/test allowlists.\n- R5. Preserve existing authentication, GitHub webhook HMAC verification,\n routing, health checks, request logging, and settings hot-reload behavior.\n- R6. Treat old IP allowlist config as invalid after removal.\n- R7. Update operator-facing docs/changelog so users know to move source-IP\n restrictions upstream.\n\n## Scope Boundaries\n\n- Do not remove `[server.auth.github].allowed_usernames`.\n- Do not remove Daytona/sandbox `cidr_allow_list` network policy support.\n- Do not remove generic uses of \"allowlist\" in policy tests, markdown rendering,\n model controls, or other unrelated domains.\n- Do not add a migration, warning-only parser, or fallback compatibility path for\n old IP allowlist settings.\n- Do not change GitHub webhook signature verification or webhook route strategy.\n\n## Context & Research\n\n### Relevant Code and Patterns\n\n- Settings use sparse config layers in `lib/crates/fabro-config/src/layers/`\n and dense resolved types in `lib/crates/fabro-types/src/settings/`.\n- The OpenAPI spec at `docs/public/api-reference/fabro-api.yaml` is the source\n of truth for API wire shape; `cargo build -p fabro-api` regenerates Rust API\n code, and `cd lib/packages/fabro-api-client && bun run generate` regenerates\n the TypeScript client.\n- `lib/crates/fabro-server/src/server.rs` builds the router and currently wires\n IP allowlist middleware before other route dispatch behavior.\n- `lib/crates/fabro-server/src/serve.rs` currently resolves server and webhook\n allowlist configs at startup and creates a GitHub `/meta` resolver.\n- `apps/fabro-web/app/routes/settings-security.tsx` displays the settings API's\n `server.ip_allowlist` state.\n\n### Main Removal Surfaces\n\n- Config/types: `fabro-types`, `fabro-config`, config tests.\n- Server runtime: `fabro-server/src/ip_allowlist.rs`, router wiring, startup\n wiring, test helpers, routing/TCP tests.\n- API/contracts: OpenAPI schema, `fabro-api` type replacements/exports, generated\n TypeScript API client models.\n- Frontend/docs: Settings > Security copy, settings navigation copy, changelog.\n\n## Key Technical Decisions\n\n- **Hard removal:** Removed config keys should fail through existing\n `deny_unknown_fields` behavior. This keeps the change simple and makes stale\n deployment config visible immediately.\n- **Delete, do not stub:** Remove `IpAllowlistConfig` and middleware parameters\n instead of passing default empty configs through the router. Empty stubs would\n keep the feature shape alive and make future cleanup harder.\n- **Keep webhook auth unchanged:** GitHub webhook source-IP filtering goes away,\n but HMAC signature verification remains the security boundary for webhook\n payload authenticity.\n- **Generated clients follow OpenAPI:** Update the OpenAPI schemas first, then\n regenerate Rust and TypeScript API outputs rather than hand-editing generated\n client files except as a short-term cleanup if generation leaves stale exports.\n- **Docs mention upstream controls:** The changelog and security guidance should\n direct operators to network-layer controls, not a replacement Fabro setting.\n\n## Implementation Units\n\n- [ ] **Unit 1: Remove config schema and resolved types**\n\n**Goal:** Delete the IP allowlist settings contract from config parsing and dense\nserver settings.\n\n**Requirements:** R2, R3, R4, R6\n\n**Dependencies:** None\n\n**Files:**\n- Modify: `lib/crates/fabro-types/src/settings/server.rs`\n- Modify: `lib/crates/fabro-types/src/settings/mod.rs`\n- Modify: `lib/crates/fabro-types/Cargo.toml`\n- Modify: `lib/crates/fabro-config/src/layers/server.rs`\n- Modify: `lib/crates/fabro-config/src/layers/mod.rs`\n- Modify: `lib/crates/fabro-config/src/lib.rs`\n- Modify: `lib/crates/fabro-config/src/resolve/server.rs`\n- Modify: `lib/crates/fabro-config/src/tests/resolve_server.rs`\n\n**Approach:**\n- Remove `ServerNamespace.ip_allowlist` and its `test_default()` population.\n- Delete `ServerIpAllowlistSettings`, `ServerIpAllowlistOverrideSettings`, and\n `IpAllowEntry`.\n- Remove `ServerLayer.ip_allowlist`, `ServerIpAllowlistLayer`,\n `ServerIpAllowlistOverrideLayer`, and `IntegrationWebhooksLayer.ip_allowlist`.\n- Remove resolver functions and validation for global and webhook IP allowlists,\n including `github_meta_hooks` parsing and Unix socket trusted-proxy checks.\n- Remove `ipnet` from `fabro-types`. Keep `ipnet` in `fabro-config` because\n `resolve/environment.rs` still validates sandbox CIDR policy.\n- Replace old positive/negative IP allowlist config tests with unknown-field\n tests for `[server.ip_allowlist]` and\n `[server.integrations.github.webhooks.ip_allowlist]`.\n\n**Patterns to follow:**\n- Existing unknown-field tests in `lib/crates/fabro-config/src/tests/resolve_server.rs`\n for retired server settings.\n- Existing `ServerLayer`/`ServerNamespace` layer-to-resolved pattern for removing\n a server subdomain cleanly.\n\n**Test scenarios:**\n- Error path: TOML with `[server.ip_allowlist]` fails parsing/resolution with an\n unknown-field diagnostic mentioning `ip_allowlist`.\n- Error path: TOML with `[server.integrations.github.webhooks.ip_allowlist]`\n fails with an unknown-field diagnostic mentioning `ip_allowlist`.\n- Happy path: minimal valid server settings still resolve without any\n `ip_allowlist` field.\n- Integration: serialized `ServerSettings` JSON no longer includes\n `server.ip_allowlist`.\n\n**Verification:**\n- `fabro-config` and `fabro-types` compile without removed IP allowlist symbols.\n- Config tests prove stale allowlist settings are rejected.\n\n- [ ] **Unit 2: Remove server middleware and startup resolution**\n\n**Goal:** Remove all runtime source-IP filtering and GitHub `/meta` range\nresolution from `fabro-server`.\n\n**Requirements:** R1, R4, R5\n\n**Dependencies:** Unit 1\n\n**Files:**\n- Delete: `lib/crates/fabro-server/src/ip_allowlist.rs`\n- Modify: `lib/crates/fabro-server/src/lib.rs`\n- Modify: `lib/crates/fabro-server/src/server.rs`\n- Modify: `lib/crates/fabro-server/src/serve.rs`\n- Modify: `lib/crates/fabro-server/src/test_support.rs`\n- Modify: `lib/crates/fabro-server/Cargo.toml`\n- Modify: `lib/crates/fabro-server/src/auth/translate.rs`\n- Modify: `lib/crates/fabro-server/src/web_auth.rs`\n- Modify: `lib/crates/fabro-cli/tests/it/support/auth_harness.rs`\n\n**Approach:**\n- Remove the public `ip_allowlist` module export.\n- Delete `IpAllowlistConfig`, `IpAllowlist`, `GitHubMetaResolver`, client-IP\n extraction, middleware, and GitHub meta cache helpers.\n- Simplify `build_router_with_options` by removing its `Arc`\n parameter and removing `RouterOptions.github_webhook_ip_allowlist`.\n- Remove the global allowlist middleware layer from the main app router.\n- Simplify `github_webhook_routes` so it only receives the webhook secret and no\n route-specific allowlist config.\n- Remove startup creation of `GitHubMetaResolver`, `default_ip_allowlist`,\n `resolve_github_webhook_ip_allowlist`, and\n `resolve_startup_github_webhook_ip_allowlist`.\n- Replace all test/helper call sites that pass `IpAllowlistConfig::default()`\n with the simplified router signature.\n- Remove `ipnet` and any now-unused HTTP mocking/test-only dependencies from\n `fabro-server` if they are only used by the deleted module.\n- Consider whether `serve.rs` still needs `SocketAddr` for TCP\n `ConnectInfo`; if it was only present for allowlisting tests, remove the\n connect-info service wrapper and imports.\n\n**Patterns to follow:**\n- Keep router layers ordered as they are after the allowlist layer is removed:\n auth translation, demo routing, auth extension, canonical host, security\n headers, HTTP logging, request ID.\n- Existing webhook route HMAC tests and auth tests should remain the source of\n truth for webhook behavior.\n\n**Test scenarios:**\n- Happy path: API requests from any TCP remote address route normally when auth\n requirements are satisfied.\n- Happy path: `/health` remains accessible.\n- Integration: GitHub webhook route still rejects missing/invalid signatures and\n accepts valid signatures exactly as before.\n- Cleanup: there are no references to `IpAllowlistConfig`, `ip_allowlist_middleware`,\n `GitHubMetaResolver`, `github_meta_hooks`, or `github-meta-hooks.json`.\n\n**Verification:**\n- `fabro-server` and CLI auth harness tests compile against the simplified router\n API.\n- Runtime startup no longer performs any GitHub `/meta` fetch for webhook IP\n ranges.\n\n- [ ] **Unit 3: Update OpenAPI and generated API clients**\n\n**Goal:** Remove IP allowlist fields and schemas from public settings API\ncontracts and regenerated clients.\n\n**Requirements:** R3, R4\n\n**Dependencies:** Units 1 and 2\n\n**Files:**\n- Modify: `docs/public/api-reference/fabro-api.yaml`\n- Modify: `lib/crates/fabro-api/build.rs`\n- Modify: `lib/crates/fabro-api/src/lib.rs`\n- Modify: `lib/crates/fabro-api/tests/server_settings_round_trip.rs`\n- Modify/generated: `lib/packages/fabro-api-client/src/models/*`\n\n**Approach:**\n- Remove `ip_allowlist` from `ServerNamespace.required` and\n `ServerNamespace.properties`.\n- Remove `ServerIpAllowlistSettings`,\n `ServerIpAllowlistOverrideSettings`, `IpAllowEntry`,\n `LiteralIpAllowEntry`, and `GitHubMetaHooksEntry` schemas.\n- Remove `IntegrationWebhooksSettings.ip_allowlist` from required/properties.\n- Remove `with_replacement` entries and public re-exports for removed types in\n `fabro-api`.\n- Regenerate Rust API code by building `fabro-api`.\n- Regenerate TypeScript Axios client and ensure stale allowlist model files and\n index exports are gone.\n- Strengthen round-trip tests to assert settings JSON omits\n `server.ip_allowlist` and webhook `ip_allowlist`.\n\n**Patterns to follow:**\n- Existing OpenAPI-first workflow in `AGENTS.md`.\n- Existing `server_settings_family_reuses_domain_types` assertions for shared\n domain type identity.\n\n**Test scenarios:**\n- Contract: `ServerSettings` round-trips through API types without any IP\n allowlist field.\n- Contract: OpenAPI-generated TypeScript `ServerNamespace` has no\n `ip_allowlist` property.\n- Contract: `IntegrationWebhooksSettings` has only webhook strategy fields after\n removal.\n\n**Verification:**\n- Generated Rust and TypeScript clients match the updated OpenAPI spec.\n- `rg \"ServerIpAllowlist|IpAllowEntry|server-ip-allowlist|literal-ip-allow-entry\"`\n finds no remaining generated/public API references.\n\n- [ ] **Unit 4: Update web settings UI**\n\n**Goal:** Remove IP allowlist display from Settings > Security and align copy\nwith the new settings shape.\n\n**Requirements:** R3, R7\n\n**Dependencies:** Unit 3\n\n**Files:**\n- Modify: `apps/fabro-web/app/routes/settings-security.tsx`\n- Modify: `apps/fabro-web/app/routes/settings.tsx`\n\n**Approach:**\n- Change the Security page description from \"Authentication methods and network\n allowlist\" to authentication-only wording.\n- Remove destructuring and rendering of `settings.server.ip_allowlist`.\n- Remove the unused `Count` and `plural` imports if no longer used.\n- Update the Settings nav description for Security from \"Authentication and\n network allowlist\" to authentication-focused copy.\n\n**Patterns to follow:**\n- Existing `settings-panel` row layout for Auth methods and Allowed usernames.\n\n**Test scenarios:**\n- Typecheck: the route compiles against the regenerated API client with no\n `server.ip_allowlist` property.\n- UI behavior: Security page still renders auth methods and allowed usernames.\n- Cleanup: no frontend references to `ip_allowlist`, `IP allowlist`, or\n `trusted_proxy_count` remain.\n\n**Verification:**\n- `apps/fabro-web` typecheck passes against the new generated client.\n\n- [ ] **Unit 5: Update tests, docs, changelog, and dependency lockfile**\n\n**Goal:** Remove stale references and document the operator-facing behavior\nchange.\n\n**Requirements:** R4, R6, R7\n\n**Dependencies:** Units 1 through 4\n\n**Files:**\n- Modify: `lib/crates/fabro-server/tests/it/api/routing.rs`\n- Modify: `lib/crates/fabro-server/tests/it/api/tcp.rs`\n- Modify: `lib/crates/fabro-server/tests/it/api/settings.rs`\n- Modify: `docs/public/administration/security.mdx`\n- Create or modify: `docs/public/changelog/2026-05-27.mdx`\n- Modify: `Cargo.lock` if dependency graph changes\n\n**Approach:**\n- Delete route/TCP tests that only prove IP allowlist enforcement or\n `ConnectInfo` behavior.\n- Adjust any router setup helpers after the signature simplification.\n- Add a settings API assertion that `server.ip_allowlist` is absent from\n `/api/v1/settings`.\n- Update security docs to explicitly state that Fabro does not provide inbound\n source-IP allowlisting and operators should use upstream network controls.\n- Add a changelog entry dated 2026-05-27 describing the hard removal and the\n expected replacement at the deployment layer.\n- Run dependency resolution after removing direct `ipnet`/test dependencies;\n keep transitive or unrelated `ipnet` entries needed by sandbox CIDR validation.\n\n**Patterns to follow:**\n- Existing changelog style in `docs/public/changelog/2026-05-26.mdx`.\n- Existing settings API integration test style in\n `lib/crates/fabro-server/tests/it/api/settings.rs`.\n\n**Test scenarios:**\n- Settings API: response contains server auth, listen, storage, scheduler, and\n integrations fields, but not `server.ip_allowlist`.\n- Config compatibility: old IP allowlist TOML fails before startup rather than\n being silently ignored.\n- Docs validation: security docs no longer imply Fabro can restrict inbound\n source IPs internally.\n\n**Verification:**\n- `rg -n \"ip_allowlist|trusted_proxy_count|github_meta_hooks|IP allowlist|ip allowlist\"`\n returns only historical archived plan/brainstorm/spec references or unrelated\n non-server allowlist text.\n\n## System-Wide Impact\n\n- **Public API:** `GET /api/v1/settings` response shape changes by removing\n `server.ip_allowlist` and webhook `ip_allowlist`.\n- **Config compatibility:** Existing `settings.toml` files containing the removed\n keys become invalid. This is intentional.\n- **Runtime security posture:** Fabro no longer blocks requests based on source\n IP. Operators must enforce network source restrictions upstream.\n- **Webhook handling:** GitHub webhook HMAC verification remains unchanged; only\n optional source-IP filtering is removed.\n- **Generated clients:** Downstream TypeScript/Rust consumers that read\n `server.ip_allowlist` must update.\n\n## Risks & Mitigations\n\n| Risk | Mitigation |\n|------|------------|\n| Operators accidentally expose a server that previously relied on Fabro IP allowlisting | Changelog and security docs explicitly call out the removal and direct operators to upstream controls. |\n| Generated API clients retain stale types | Update OpenAPI first, regenerate both Rust and TypeScript clients, and run search checks for stale symbols. |\n| Unrelated allowlist functionality is removed by broad search/replace | Scope searches to `IpAllow`, `ip_allowlist`, `trusted_proxy_count`, and `github_meta_hooks`; preserve sandbox CIDR and GitHub username allowlists. |\n| Router signature cleanup breaks many tests | Update shared test helpers first, then compile-driven cleanup of remaining call sites. |\n| `ipnet` is removed where still needed | Keep `fabro-config` dependency if environment CIDR validation still imports `ipnet::IpNet`. |\n\n## Test Plan\n\n- `cargo build -p fabro-api`\n- `cd lib/packages/fabro-api-client && bun run generate`\n- `cargo nextest run -p fabro-config -p fabro-types -p fabro-api -p fabro-server`\n- `cd apps/fabro-web && bun run typecheck`\n- `cd apps/fabro-web && bun test`\n- `cargo +nightly-2026-04-14 fmt --check --all`\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings`\n\n## Assumptions\n\n- The accepted compatibility policy is hard removal: no warning-only parser, no\n migration, and no custom compatibility error path.\n- Public API consumers can tolerate a breaking settings shape change for this\n removed feature.\n- Historical documents in `docs/plans/`, `docs/brainstorms/`, and\n `docs/superpowers/` are archival and do not need rewriting.\n\n## Sources & References\n\n- Previous feature requirements:\n `docs/brainstorms/2026-04-15-ip-whitelist-requirements.md`\n- Previous implementation plan:\n `docs/plans/2026-04-15-002-feat-ip-allowlist-plan.md`\n- API workflow guidance: `AGENTS.md`\n- OpenAPI source of truth: `docs/public/api-reference/fabro-api.yaml`\n", + "graph.model_stylesheet": "\n * { model: claude-opus-4-7; }\n ", "current_node": "preflight_lint", + "failure_signature": "", + "outcome": "succeeded", + "thread.preflight_compile.current_node": "preflight_lint", + "internal.fidelity": "compact", + "internal.retry_count.toolchain": 0, + "internal.run_id": "01KSNYVQCKXPCSCEGWMZ1Q574Z", + "internal.thread_id": "preflight_compile", + "internal.work_dir": "/home/daytona/workspace/fabro", + "command.output": "blob://sha256/12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "internal.node_visit_count": 1, + "internal.retry_count.preflight_compile": 0, + "failure_class": "" + }, + "node_outcomes": { + "start": { + "status": "succeeded", + "usage": null + }, + "toolchain": { + "status": "succeeded", + "context_updates": { + "command.output": "blob://sha256/fc14b2ba2d770e5cd3169df7a29525c962adfc4cfa3097b9098c63ebd61a748c" + }, + "notes": "Script completed: command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", + "usage": null, + "timing": { + "wall_time_ms": 0, + "inference_time_ms": 0, + "tool_time_ms": 1482, + "active_time_ms": 1482 + } + }, + "preflight_compile": { + "status": "succeeded", + "context_updates": { + "command.output": "blob://sha256/12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126" + }, + "notes": "Script completed: cargo check -q --workspace 2>&1", + "usage": null, + "timing": { + "wall_time_ms": 0, + "inference_time_ms": 0, + "tool_time_ms": 135614, + "active_time_ms": 135614 + } + }, + "preflight_lint": { + "status": "succeeded", + "context_updates": { + "command.output": "blob://sha256/12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126" + }, + "notes": "Script completed: cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", + "usage": null, + "timing": { + "wall_time_ms": 0, + "inference_time_ms": 0, + "tool_time_ms": 142983, + "active_time_ms": 142983 + } + } + }, + "next_node_id": "implement", + "git_commit_sha": "00a7336c611cbf8da8f670404e8d053802cbac6d", + "node_visits": { + "preflight_compile": 1, + "preflight_lint": 1, + "toolchain": 1, + "start": 1 + } + }, + "diff": { + "summary": { + "files_changed": 0, + "additions": 0, + "deletions": 0 + } + } + }, + { + "seq": 0, + "checkpoint": { + "timestamp": "2026-05-28T00:52:56.818587Z", + "current_node": "implement", + "completed_nodes": [ + "start", + "toolchain", + "preflight_compile", + "preflight_lint", + "implement" + ], + "node_retries": {}, + "context_values": { + "thread.start.current_node": "toolchain", + "current_node": "implement", "failure_class": "", "internal.work_dir": "/home/daytona/workspace/fabro", "graph.goal": "---\ntitle: \"refactor: Remove IP allowlisting\"\ntype: refactor\nstatus: active\ndate: 2026-05-27\n---\n\n# refactor: Remove IP allowlisting\n\n## Overview\n\nRemove Fabro's inbound server IP allowlisting feature completely. This removes the\nserver-wide `[server.ip_allowlist]` setting, the GitHub webhook-specific\n`[server.integrations.github.webhooks.ip_allowlist]` overlay, request middleware,\nclient-IP extraction, GitHub `/meta` hook-range expansion, settings API exposure,\ngenerated API client types, and the Settings > Security UI display.\n\nExisting configs that still contain removed IP allowlist keys should fail as\nunknown fields. This is an intentional hard removal, not a compatibility\ndeprecation.\n\n## Problem Frame\n\nThe feature is being removed from Fabro rather than maintained as an in-process\nnetwork access-control layer. Network source restrictions should be handled\noutside Fabro by reverse proxies, firewalls, VPNs, Tailscale, platform ingress,\nor other deployment-layer controls.\n\n## Requirements Trace\n\n- R1. Remove all runtime enforcement of inbound source-IP allowlisting from web,\n API, static asset, and GitHub webhook routes.\n- R2. Remove the server settings schema for global and GitHub webhook IP\n allowlists.\n- R3. Remove `IpAllowEntry` and related API schema/client types from public\n settings payloads.\n- R4. Keep unrelated allowlists intact: GitHub username allowlists, sandbox\n egress CIDR allow lists, and internal policy/test allowlists.\n- R5. Preserve existing authentication, GitHub webhook HMAC verification,\n routing, health checks, request logging, and settings hot-reload behavior.\n- R6. Treat old IP allowlist config as invalid after removal.\n- R7. Update operator-facing docs/changelog so users know to move source-IP\n restrictions upstream.\n\n## Scope Boundaries\n\n- Do not remove `[server.auth.github].allowed_usernames`.\n- Do not remove Daytona/sandbox `cidr_allow_list` network policy support.\n- Do not remove generic uses of \"allowlist\" in policy tests, markdown rendering,\n model controls, or other unrelated domains.\n- Do not add a migration, warning-only parser, or fallback compatibility path for\n old IP allowlist settings.\n- Do not change GitHub webhook signature verification or webhook route strategy.\n\n## Context & Research\n\n### Relevant Code and Patterns\n\n- Settings use sparse config layers in `lib/crates/fabro-config/src/layers/`\n and dense resolved types in `lib/crates/fabro-types/src/settings/`.\n- The OpenAPI spec at `docs/public/api-reference/fabro-api.yaml` is the source\n of truth for API wire shape; `cargo build -p fabro-api` regenerates Rust API\n code, and `cd lib/packages/fabro-api-client && bun run generate` regenerates\n the TypeScript client.\n- `lib/crates/fabro-server/src/server.rs` builds the router and currently wires\n IP allowlist middleware before other route dispatch behavior.\n- `lib/crates/fabro-server/src/serve.rs` currently resolves server and webhook\n allowlist configs at startup and creates a GitHub `/meta` resolver.\n- `apps/fabro-web/app/routes/settings-security.tsx` displays the settings API's\n `server.ip_allowlist` state.\n\n### Main Removal Surfaces\n\n- Config/types: `fabro-types`, `fabro-config`, config tests.\n- Server runtime: `fabro-server/src/ip_allowlist.rs`, router wiring, startup\n wiring, test helpers, routing/TCP tests.\n- API/contracts: OpenAPI schema, `fabro-api` type replacements/exports, generated\n TypeScript API client models.\n- Frontend/docs: Settings > Security copy, settings navigation copy, changelog.\n\n## Key Technical Decisions\n\n- **Hard removal:** Removed config keys should fail through existing\n `deny_unknown_fields` behavior. This keeps the change simple and makes stale\n deployment config visible immediately.\n- **Delete, do not stub:** Remove `IpAllowlistConfig` and middleware parameters\n instead of passing default empty configs through the router. Empty stubs would\n keep the feature shape alive and make future cleanup harder.\n- **Keep webhook auth unchanged:** GitHub webhook source-IP filtering goes away,\n but HMAC signature verification remains the security boundary for webhook\n payload authenticity.\n- **Generated clients follow OpenAPI:** Update the OpenAPI schemas first, then\n regenerate Rust and TypeScript API outputs rather than hand-editing generated\n client files except as a short-term cleanup if generation leaves stale exports.\n- **Docs mention upstream controls:** The changelog and security guidance should\n direct operators to network-layer controls, not a replacement Fabro setting.\n\n## Implementation Units\n\n- [ ] **Unit 1: Remove config schema and resolved types**\n\n**Goal:** Delete the IP allowlist settings contract from config parsing and dense\nserver settings.\n\n**Requirements:** R2, R3, R4, R6\n\n**Dependencies:** None\n\n**Files:**\n- Modify: `lib/crates/fabro-types/src/settings/server.rs`\n- Modify: `lib/crates/fabro-types/src/settings/mod.rs`\n- Modify: `lib/crates/fabro-types/Cargo.toml`\n- Modify: `lib/crates/fabro-config/src/layers/server.rs`\n- Modify: `lib/crates/fabro-config/src/layers/mod.rs`\n- Modify: `lib/crates/fabro-config/src/lib.rs`\n- Modify: `lib/crates/fabro-config/src/resolve/server.rs`\n- Modify: `lib/crates/fabro-config/src/tests/resolve_server.rs`\n\n**Approach:**\n- Remove `ServerNamespace.ip_allowlist` and its `test_default()` population.\n- Delete `ServerIpAllowlistSettings`, `ServerIpAllowlistOverrideSettings`, and\n `IpAllowEntry`.\n- Remove `ServerLayer.ip_allowlist`, `ServerIpAllowlistLayer`,\n `ServerIpAllowlistOverrideLayer`, and `IntegrationWebhooksLayer.ip_allowlist`.\n- Remove resolver functions and validation for global and webhook IP allowlists,\n including `github_meta_hooks` parsing and Unix socket trusted-proxy checks.\n- Remove `ipnet` from `fabro-types`. Keep `ipnet` in `fabro-config` because\n `resolve/environment.rs` still validates sandbox CIDR policy.\n- Replace old positive/negative IP allowlist config tests with unknown-field\n tests for `[server.ip_allowlist]` and\n `[server.integrations.github.webhooks.ip_allowlist]`.\n\n**Patterns to follow:**\n- Existing unknown-field tests in `lib/crates/fabro-config/src/tests/resolve_server.rs`\n for retired server settings.\n- Existing `ServerLayer`/`ServerNamespace` layer-to-resolved pattern for removing\n a server subdomain cleanly.\n\n**Test scenarios:**\n- Error path: TOML with `[server.ip_allowlist]` fails parsing/resolution with an\n unknown-field diagnostic mentioning `ip_allowlist`.\n- Error path: TOML with `[server.integrations.github.webhooks.ip_allowlist]`\n fails with an unknown-field diagnostic mentioning `ip_allowlist`.\n- Happy path: minimal valid server settings still resolve without any\n `ip_allowlist` field.\n- Integration: serialized `ServerSettings` JSON no longer includes\n `server.ip_allowlist`.\n\n**Verification:**\n- `fabro-config` and `fabro-types` compile without removed IP allowlist symbols.\n- Config tests prove stale allowlist settings are rejected.\n\n- [ ] **Unit 2: Remove server middleware and startup resolution**\n\n**Goal:** Remove all runtime source-IP filtering and GitHub `/meta` range\nresolution from `fabro-server`.\n\n**Requirements:** R1, R4, R5\n\n**Dependencies:** Unit 1\n\n**Files:**\n- Delete: `lib/crates/fabro-server/src/ip_allowlist.rs`\n- Modify: `lib/crates/fabro-server/src/lib.rs`\n- Modify: `lib/crates/fabro-server/src/server.rs`\n- Modify: `lib/crates/fabro-server/src/serve.rs`\n- Modify: `lib/crates/fabro-server/src/test_support.rs`\n- Modify: `lib/crates/fabro-server/Cargo.toml`\n- Modify: `lib/crates/fabro-server/src/auth/translate.rs`\n- Modify: `lib/crates/fabro-server/src/web_auth.rs`\n- Modify: `lib/crates/fabro-cli/tests/it/support/auth_harness.rs`\n\n**Approach:**\n- Remove the public `ip_allowlist` module export.\n- Delete `IpAllowlistConfig`, `IpAllowlist`, `GitHubMetaResolver`, client-IP\n extraction, middleware, and GitHub meta cache helpers.\n- Simplify `build_router_with_options` by removing its `Arc`\n parameter and removing `RouterOptions.github_webhook_ip_allowlist`.\n- Remove the global allowlist middleware layer from the main app router.\n- Simplify `github_webhook_routes` so it only receives the webhook secret and no\n route-specific allowlist config.\n- Remove startup creation of `GitHubMetaResolver`, `default_ip_allowlist`,\n `resolve_github_webhook_ip_allowlist`, and\n `resolve_startup_github_webhook_ip_allowlist`.\n- Replace all test/helper call sites that pass `IpAllowlistConfig::default()`\n with the simplified router signature.\n- Remove `ipnet` and any now-unused HTTP mocking/test-only dependencies from\n `fabro-server` if they are only used by the deleted module.\n- Consider whether `serve.rs` still needs `SocketAddr` for TCP\n `ConnectInfo`; if it was only present for allowlisting tests, remove the\n connect-info service wrapper and imports.\n\n**Patterns to follow:**\n- Keep router layers ordered as they are after the allowlist layer is removed:\n auth translation, demo routing, auth extension, canonical host, security\n headers, HTTP logging, request ID.\n- Existing webhook route HMAC tests and auth tests should remain the source of\n truth for webhook behavior.\n\n**Test scenarios:**\n- Happy path: API requests from any TCP remote address route normally when auth\n requirements are satisfied.\n- Happy path: `/health` remains accessible.\n- Integration: GitHub webhook route still rejects missing/invalid signatures and\n accepts valid signatures exactly as before.\n- Cleanup: there are no references to `IpAllowlistConfig`, `ip_allowlist_middleware`,\n `GitHubMetaResolver`, `github_meta_hooks`, or `github-meta-hooks.json`.\n\n**Verification:**\n- `fabro-server` and CLI auth harness tests compile against the simplified router\n API.\n- Runtime startup no longer performs any GitHub `/meta` fetch for webhook IP\n ranges.\n\n- [ ] **Unit 3: Update OpenAPI and generated API clients**\n\n**Goal:** Remove IP allowlist fields and schemas from public settings API\ncontracts and regenerated clients.\n\n**Requirements:** R3, R4\n\n**Dependencies:** Units 1 and 2\n\n**Files:**\n- Modify: `docs/public/api-reference/fabro-api.yaml`\n- Modify: `lib/crates/fabro-api/build.rs`\n- Modify: `lib/crates/fabro-api/src/lib.rs`\n- Modify: `lib/crates/fabro-api/tests/server_settings_round_trip.rs`\n- Modify/generated: `lib/packages/fabro-api-client/src/models/*`\n\n**Approach:**\n- Remove `ip_allowlist` from `ServerNamespace.required` and\n `ServerNamespace.properties`.\n- Remove `ServerIpAllowlistSettings`,\n `ServerIpAllowlistOverrideSettings`, `IpAllowEntry`,\n `LiteralIpAllowEntry`, and `GitHubMetaHooksEntry` schemas.\n- Remove `IntegrationWebhooksSettings.ip_allowlist` from required/properties.\n- Remove `with_replacement` entries and public re-exports for removed types in\n `fabro-api`.\n- Regenerate Rust API code by building `fabro-api`.\n- Regenerate TypeScript Axios client and ensure stale allowlist model files and\n index exports are gone.\n- Strengthen round-trip tests to assert settings JSON omits\n `server.ip_allowlist` and webhook `ip_allowlist`.\n\n**Patterns to follow:**\n- Existing OpenAPI-first workflow in `AGENTS.md`.\n- Existing `server_settings_family_reuses_domain_types` assertions for shared\n domain type identity.\n\n**Test scenarios:**\n- Contract: `ServerSettings` round-trips through API types without any IP\n allowlist field.\n- Contract: OpenAPI-generated TypeScript `ServerNamespace` has no\n `ip_allowlist` property.\n- Contract: `IntegrationWebhooksSettings` has only webhook strategy fields after\n removal.\n\n**Verification:**\n- Generated Rust and TypeScript clients match the updated OpenAPI spec.\n- `rg \"ServerIpAllowlist|IpAllowEntry|server-ip-allowlist|literal-ip-allow-entry\"`\n finds no remaining generated/public API references.\n\n- [ ] **Unit 4: Update web settings UI**\n\n**Goal:** Remove IP allowlist display from Settings > Security and align copy\nwith the new settings shape.\n\n**Requirements:** R3, R7\n\n**Dependencies:** Unit 3\n\n**Files:**\n- Modify: `apps/fabro-web/app/routes/settings-security.tsx`\n- Modify: `apps/fabro-web/app/routes/settings.tsx`\n\n**Approach:**\n- Change the Security page description from \"Authentication methods and network\n allowlist\" to authentication-only wording.\n- Remove destructuring and rendering of `settings.server.ip_allowlist`.\n- Remove the unused `Count` and `plural` imports if no longer used.\n- Update the Settings nav description for Security from \"Authentication and\n network allowlist\" to authentication-focused copy.\n\n**Patterns to follow:**\n- Existing `settings-panel` row layout for Auth methods and Allowed usernames.\n\n**Test scenarios:**\n- Typecheck: the route compiles against the regenerated API client with no\n `server.ip_allowlist` property.\n- UI behavior: Security page still renders auth methods and allowed usernames.\n- Cleanup: no frontend references to `ip_allowlist`, `IP allowlist`, or\n `trusted_proxy_count` remain.\n\n**Verification:**\n- `apps/fabro-web` typecheck passes against the new generated client.\n\n- [ ] **Unit 5: Update tests, docs, changelog, and dependency lockfile**\n\n**Goal:** Remove stale references and document the operator-facing behavior\nchange.\n\n**Requirements:** R4, R6, R7\n\n**Dependencies:** Units 1 through 4\n\n**Files:**\n- Modify: `lib/crates/fabro-server/tests/it/api/routing.rs`\n- Modify: `lib/crates/fabro-server/tests/it/api/tcp.rs`\n- Modify: `lib/crates/fabro-server/tests/it/api/settings.rs`\n- Modify: `docs/public/administration/security.mdx`\n- Create or modify: `docs/public/changelog/2026-05-27.mdx`\n- Modify: `Cargo.lock` if dependency graph changes\n\n**Approach:**\n- Delete route/TCP tests that only prove IP allowlist enforcement or\n `ConnectInfo` behavior.\n- Adjust any router setup helpers after the signature simplification.\n- Add a settings API assertion that `server.ip_allowlist` is absent from\n `/api/v1/settings`.\n- Update security docs to explicitly state that Fabro does not provide inbound\n source-IP allowlisting and operators should use upstream network controls.\n- Add a changelog entry dated 2026-05-27 describing the hard removal and the\n expected replacement at the deployment layer.\n- Run dependency resolution after removing direct `ipnet`/test dependencies;\n keep transitive or unrelated `ipnet` entries needed by sandbox CIDR validation.\n\n**Patterns to follow:**\n- Existing changelog style in `docs/public/changelog/2026-05-26.mdx`.\n- Existing settings API integration test style in\n `lib/crates/fabro-server/tests/it/api/settings.rs`.\n\n**Test scenarios:**\n- Settings API: response contains server auth, listen, storage, scheduler, and\n integrations fields, but not `server.ip_allowlist`.\n- Config compatibility: old IP allowlist TOML fails before startup rather than\n being silently ignored.\n- Docs validation: security docs no longer imply Fabro can restrict inbound\n source IPs internally.\n\n**Verification:**\n- `rg -n \"ip_allowlist|trusted_proxy_count|github_meta_hooks|IP allowlist|ip allowlist\"`\n returns only historical archived plan/brainstorm/spec references or unrelated\n non-server allowlist text.\n\n## System-Wide Impact\n\n- **Public API:** `GET /api/v1/settings` response shape changes by removing\n `server.ip_allowlist` and webhook `ip_allowlist`.\n- **Config compatibility:** Existing `settings.toml` files containing the removed\n keys become invalid. This is intentional.\n- **Runtime security posture:** Fabro no longer blocks requests based on source\n IP. Operators must enforce network source restrictions upstream.\n- **Webhook handling:** GitHub webhook HMAC verification remains unchanged; only\n optional source-IP filtering is removed.\n- **Generated clients:** Downstream TypeScript/Rust consumers that read\n `server.ip_allowlist` must update.\n\n## Risks & Mitigations\n\n| Risk | Mitigation |\n|------|------------|\n| Operators accidentally expose a server that previously relied on Fabro IP allowlisting | Changelog and security docs explicitly call out the removal and direct operators to upstream controls. |\n| Generated API clients retain stale types | Update OpenAPI first, regenerate both Rust and TypeScript clients, and run search checks for stale symbols. |\n| Unrelated allowlist functionality is removed by broad search/replace | Scope searches to `IpAllow`, `ip_allowlist`, `trusted_proxy_count`, and `github_meta_hooks`; preserve sandbox CIDR and GitHub username allowlists. |\n| Router signature cleanup breaks many tests | Update shared test helpers first, then compile-driven cleanup of remaining call sites. |\n| `ipnet` is removed where still needed | Keep `fabro-config` dependency if environment CIDR validation still imports `ipnet::IpNet`. |\n\n## Test Plan\n\n- `cargo build -p fabro-api`\n- `cd lib/packages/fabro-api-client && bun run generate`\n- `cargo nextest run -p fabro-config -p fabro-types -p fabro-api -p fabro-server`\n- `cd apps/fabro-web && bun run typecheck`\n- `cd apps/fabro-web && bun test`\n- `cargo +nightly-2026-04-14 fmt --check --all`\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings`\n\n## Assumptions\n\n- The accepted compatibility policy is hard removal: no warning-only parser, no\n migration, and no custom compatibility error path.\n- Public API consumers can tolerate a breaking settings shape change for this\n removed feature.\n- Historical documents in `docs/plans/`, `docs/brainstorms/`, and\n `docs/superpowers/` are archival and do not need rewriting.\n\n## Sources & References\n\n- Previous feature requirements:\n `docs/brainstorms/2026-04-15-ip-whitelist-requirements.md`\n- Previous implementation plan:\n `docs/plans/2026-04-15-002-feat-ip-allowlist-plan.md`\n- API workflow guidance: `AGENTS.md`\n- OpenAPI source of truth: `docs/public/api-reference/fabro-api.yaml`\n", @@ -714,12 +814,17 @@ "command.output": "blob://sha256/12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", "internal.retry_count.preflight_compile": 0, "internal.retry_count.preflight_lint": 0, + "last_stage": "implement", + "internal.retry_count.implement": 0, "graph.rankdir": "LR", "thread.preflight_compile.current_node": "preflight_lint", + "thread.preflight_lint.current_node": "implement", "internal.retry_count.toolchain": 0, + "response.implement": "Implemented the full IP allowlisting removal refactor.\n\nSummary:\n- Removed inbound source-IP allowlist config from dense settings and sparse config layers:\n - `[server.ip_allowlist]`\n - `[server.integrations.github.webhooks.ip_allowlist]`\n - `IpAllowEntry` and related settings/types.\n- Deleted server runtime enforcement:\n - Removed `ip_allowlist` middleware/module.\n - Removed `IpAllowlistConfig`, client-IP extraction, trusted proxy handling, and GitHub `/meta` hook range resolution.\n - Simplified router/startup wiring and removed `ConnectInfo` serving requirements that only existed for allowlisting.\n- Preserved unrelated allowlists:\n - GitHub username allowlists remain.\n - Sandbox/Daytona CIDR allow lists remain.\n- Updated OpenAPI and generated clients:\n - Removed allowlist schemas/fields from `fabro-api.yaml`.\n - Regenerated Rust API and TypeScript API client.\n - Deleted stale generated TS allowlist model files.\n- Updated web UI:\n - Removed Settings > Security network allowlist display.\n - Adjusted Security page/nav copy.\n- Updated tests/docs:\n - Added unknown-field tests proving stale IP allowlist config is now rejected.\n - Added settings API assertions that removed fields are absent.\n - Updated security docs and changelog to direct operators to upstream network controls.\n- Updated dependency graph:\n - Removed direct `ipnet` deps from `fabro-types` and `fabro-server`; retained transitive/use in `fabro-config` for sandbox CIDR validation.\n\nRed/green TDD:\n- Added stale-config rejection tests and verified they failed before schema removal, then passed after removal.\n\nValidation run:\n- `cargo build -p fabro-api`\n- `cd lib/packages/fabro-api-client && bun run generate`\n- `cargo build -p fabro-cli --bin fabro`\n- `cargo nextest run -p fabro-config -p fabro-types -p fabro-api -p fabro-server --no-fail-fast`\n- `cargo +nightly-2026-04-14 fmt --check --all`\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings`\n- `git diff --check`\n- Stale-symbol search confirmed remaining `ip_allowlist`/IP allowlist references are only intentional docs/tests/assertions.\n\nNo commits were created.", "thread.toolchain.current_node": "preflight_compile", "outcome": "succeeded", - "internal.thread_id": "preflight_compile" + "last_response": "Implemented the full IP allowlisting removal refactor.\n\nSummary:\n- Removed inbound source-IP allowlist config from dense settings and sparse config layers:\n - `[server.ip_allowlist]`\n - `[server.int", + "internal.thread_id": "preflight_lint" }, "node_outcomes": { "preflight_lint": { @@ -767,11 +872,51 @@ "tool_time_ms": 135614, "active_time_ms": 135614 } + }, + "implement": { + "status": "succeeded", + "context_updates": { + "last_stage": "implement", + "last_response": "Implemented the full IP allowlisting removal refactor.\n\nSummary:\n- Removed inbound source-IP allowlist config from dense settings and sparse config layers:\n - `[server.ip_allowlist]`\n - `[server.int", + "response.implement": "Implemented the full IP allowlisting removal refactor.\n\nSummary:\n- Removed inbound source-IP allowlist config from dense settings and sparse config layers:\n - `[server.ip_allowlist]`\n - `[server.integrations.github.webhooks.ip_allowlist]`\n - `IpAllowEntry` and related settings/types.\n- Deleted server runtime enforcement:\n - Removed `ip_allowlist` middleware/module.\n - Removed `IpAllowlistConfig`, client-IP extraction, trusted proxy handling, and GitHub `/meta` hook range resolution.\n - Simplified router/startup wiring and removed `ConnectInfo` serving requirements that only existed for allowlisting.\n- Preserved unrelated allowlists:\n - GitHub username allowlists remain.\n - Sandbox/Daytona CIDR allow lists remain.\n- Updated OpenAPI and generated clients:\n - Removed allowlist schemas/fields from `fabro-api.yaml`.\n - Regenerated Rust API and TypeScript API client.\n - Deleted stale generated TS allowlist model files.\n- Updated web UI:\n - Removed Settings > Security network allowlist display.\n - Adjusted Security page/nav copy.\n- Updated tests/docs:\n - Added unknown-field tests proving stale IP allowlist config is now rejected.\n - Added settings API assertions that removed fields are absent.\n - Updated security docs and changelog to direct operators to upstream network controls.\n- Updated dependency graph:\n - Removed direct `ipnet` deps from `fabro-types` and `fabro-server`; retained transitive/use in `fabro-config` for sandbox CIDR validation.\n\nRed/green TDD:\n- Added stale-config rejection tests and verified they failed before schema removal, then passed after removal.\n\nValidation run:\n- `cargo build -p fabro-api`\n- `cd lib/packages/fabro-api-client && bun run generate`\n- `cargo build -p fabro-cli --bin fabro`\n- `cargo nextest run -p fabro-config -p fabro-types -p fabro-api -p fabro-server --no-fail-fast`\n- `cargo +nightly-2026-04-14 fmt --check --all`\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings`\n- `git diff --check`\n- Stale-symbol search confirmed remaining `ip_allowlist`/IP allowlist references are only intentional docs/tests/assertions.\n\nNo commits were created." + }, + "notes": "Stage completed: implement", + "usage": { + "input": { + "usage": { + "model": { + "provider": "openai", + "model_id": "gpt-5.5" + }, + "tokens": { + "input_tokens": 2556897, + "output_tokens": 26084, + "reasoning_tokens": 9280, + "cache_read_tokens": 17923072, + "cache_write_tokens": 0 + } + }, + "facts": { + "algorithm": "openai" + } + }, + "total_usd_micros": 22806941 + }, + "files_touched": [ + "/home/daytona/workspace/fabro/docs/public/changelog/2026-05-27.mdx" + ], + "timing": { + "wall_time_ms": 0, + "inference_time_ms": 1238818, + "tool_time_ms": 594726, + "active_time_ms": 1833544 + } } }, - "next_node_id": "implement", + "next_node_id": "simplify_opus", "node_visits": { "start": 1, + "implement": 1, "toolchain": 1, "preflight_compile": 1, "preflight_lint": 1 @@ -941,7 +1086,12 @@ "first_event_seq": 41, "prompt": null, "response": null, - "completion": null, + "completion": { + "outcome": "succeeded", + "notes": "Script completed: cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", + "failure_reason": null, + "timestamp": "2026-05-28T00:18:58.021803Z" + }, "provider_used": null, "diff": null, "script_invocation": { @@ -949,11 +1099,27 @@ "command": "exec 2>&1\ncargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", "language": "shell" }, - "script_timing": null, + "script_timing": { + "output": "blob://sha256/12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "exit_code": 0, + "duration_ms": 142983, + "termination": "exited", + "output_bytes": 0, + "live_streaming": false + }, "parallel_results": null, "output": null, + "output_bytes": 0, + "live_streaming": false, + "termination": "exited", "started_at": "2026-05-28T00:16:35.033240Z", "handler": "command", + "timing": { + "wall_time_ms": 142988, + "inference_time_ms": 0, + "tool_time_ms": 142983, + "active_time_ms": 142983 + }, "usage": { "input_tokens": 0, "output_tokens": 0, @@ -962,6 +1128,249 @@ "cache_read_tokens": 0, "cache_write_tokens": 0 }, + "state": "succeeded" + }, + "implement@1": { + "first_event_seq": 51, + "prompt": null, + "response": null, + "completion": null, + "provider_used": { + "mode": "agent", + "provider": "openai", + "model": "gpt-5.5", + "reasoning_effort": "xhigh" + }, + "diff": null, + "script_invocation": null, + "script_timing": null, + "parallel_results": null, + "output": null, + "started_at": "2026-05-28T00:19:01.909610Z", + "handler": "agent", + "usage": { + "input_tokens": 2556897, + "output_tokens": 26084, + "total_tokens": 20515333, + "reasoning_tokens": 9280, + "cache_read_tokens": 17923072, + "cache_write_tokens": 0, + "total_usd_micros": 22806941 + }, + "model": { + "provider": "openai", + "model_id": "gpt-5.5" + }, + "todos": { + "kind": "openai_plan", + "list_id": "openai_plan:be67232e-29cc-403c-ae14-84e1292cc798", + "items": [ + { + "id": "93a1f33a92044523", + "status": "completed", + "order": 0, + "subject": "Remove config schema and resolved types" + }, + { + "id": "9c1c39a1c54a1964", + "status": "completed", + "order": 1, + "subject": "Remove server middleware and startup resolution" + }, + { + "id": "c40037d2544ef745", + "status": "completed", + "order": 2, + "subject": "Update OpenAPI and generated API clients" + }, + { + "id": "f36afa2e69977883", + "status": "completed", + "order": 3, + "subject": "Update web settings UI" + }, + { + "id": "a0e7d85e34e3da02", + "status": "completed", + "order": 4, + "subject": "Update tests, docs, changelog, and dependency lockfile" + }, + { + "id": "62a37aa43f82a6c3", + "status": "completed", + "order": 5, + "subject": "Run final verification and review diff" + } + ] + }, + "permission_level": "full", + "agent_tools": [ + { + "name": "apply_patch", + "description": "Use the `apply_patch` tool to edit files. This is a FREEFORM tool, so do not wrap the patch in JSON.", + "source": { + "kind": "native" + }, + "category": "write", + "invoked": true + }, + { + "name": "close_agent", + "description": "Close a running subagent that is no longer needed.", + "source": { + "kind": "native" + }, + "category": "subagent", + "invoked": false + }, + { + "name": "glob", + "description": "Find files by file names using a glob pattern. Use path to choose the search root. Prefer this over shell find or ls when locating repository files.", + "source": { + "kind": "native" + }, + "category": "read", + "invoked": true + }, + { + "name": "grep", + "description": "Search file contents with a regex pattern. Use path to choose the search root, glob_filter to limit matching files, case_insensitive for case folding, and max_results to cap output.", + "source": { + "kind": "native" + }, + "category": "read", + "invoked": true + }, + { + "name": "read_file", + "description": "Read files before editing them. Returns line-numbered text and supports offset/limit for large files. Use this instead of shell cat, head, tail, or sed when inspecting repository files.", + "source": { + "kind": "native" + }, + "category": "read", + "invoked": true + }, + { + "name": "request_user_input", + "description": "Ask the human one or more questions and wait for their answers before continuing this stage.", + "source": { + "kind": "native" + }, + "category": "other", + "invoked": false + }, + { + "name": "send_input", + "description": "Send a follow-up message to a running subagent when new information or corrected instructions are needed.", + "source": { + "kind": "native" + }, + "category": "subagent", + "invoked": false + }, + { + "name": "shell", + "description": "Execute shell commands for terminal operations, package managers, tests and builds. Use dedicated tools for file reads, file edits, filename searches, and content searches. Provide timeout_ms for long-running commands.", + "source": { + "kind": "native" + }, + "category": "shell", + "invoked": true + }, + { + "name": "spawn_agent", + "description": "Spawn a subagent for independent work or context isolation. Use it for tasks that can proceed separately, and avoid duplicating the same work in the parent session.", + "source": { + "kind": "native" + }, + "category": "subagent", + "invoked": false + }, + { + "name": "update_plan", + "description": "Update the multi-step plan for the current task. Submit the entire plan; existing steps are reconciled by exact step text.", + "source": { + "kind": "native" + }, + "category": "other", + "invoked": true + }, + { + "name": "wait", + "description": "Wait for a subagent to complete, then use the result to synthesize the outcome for the user.", + "source": { + "kind": "native" + }, + "category": "subagent", + "invoked": false + }, + { + "name": "web_fetch", + "description": "Fetch content from a URL that starts with http:// or https://. Pass a prompt to extract specific information or summarize the page; omit prompt to return the page content.", + "source": { + "kind": "native" + }, + "category": "other", + "invoked": false + }, + { + "name": "web_search", + "description": "Search the web using Brave Search when current external information is needed. Returns result titles, URLs, and descriptions; use web_fetch for a specific URL.", + "source": { + "kind": "native" + }, + "category": "other", + "invoked": false + }, + { + "name": "write_file", + "description": "Create new files, or overwrite an existing file only when replacement is explicitly intended. Prefer edit_file for targeted changes to existing files because write_file overwrites the full file content.", + "source": { + "kind": "native" + }, + "category": "write", + "invoked": true + } + ], + "context_window": { + "provider": "openai", + "model": "gpt-5.5", + "context_window_tokens": 272000, + "input_tokens": 70749, + "usage_percent": 26.010661764705883, + "count_method": "response_usage_scaled_breakdown", + "staleness": "live", + "generated_at": "2026-05-28T00:52:56.713576Z", + "event_seq": 700, + "breakdown": [ + { + "category": "system_prompt", + "tokens": 1079, + "usage_percent": 0.3966911764705882 + }, + { + "category": "tools", + "tokens": 1541, + "usage_percent": 0.5665441176470588 + }, + { + "category": "memory", + "tokens": 3669, + "usage_percent": 1.3488970588235294 + }, + { + "category": "conversation", + "tokens": 64452, + "usage_percent": 23.695588235294117 + }, + { + "category": "other", + "tokens": 8, + "usage_percent": 0.0029411764705882353 + } + ], + "warnings": [] + }, "state": "running" } } diff --git a/stages/004-preflight_lint@1/output.log b/stages/004-preflight_lint@1/output.log new file mode 100644 index 000000000..d87ba9545 --- /dev/null +++ b/stages/004-preflight_lint@1/output.log @@ -0,0 +1 @@ +blob://sha256/12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126 \ No newline at end of file diff --git a/stages/004-preflight_lint@1/script_timing.json b/stages/004-preflight_lint@1/script_timing.json new file mode 100644 index 000000000..61611b5ad --- /dev/null +++ b/stages/004-preflight_lint@1/script_timing.json @@ -0,0 +1,8 @@ +{ + "output": "blob://sha256/12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126", + "exit_code": 0, + "duration_ms": 142983, + "termination": "exited", + "output_bytes": 0, + "live_streaming": false +} \ No newline at end of file diff --git a/stages/004-preflight_lint@1/status.json b/stages/004-preflight_lint@1/status.json new file mode 100644 index 000000000..b2fa6e701 --- /dev/null +++ b/stages/004-preflight_lint@1/status.json @@ -0,0 +1,6 @@ +{ + "outcome": "succeeded", + "notes": "Script completed: cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", + "failure_reason": null, + "timestamp": "2026-05-28T00:18:58.021803Z" +} \ No newline at end of file diff --git a/stages/005-implement@1/prompt.md b/stages/005-implement@1/prompt.md new file mode 100644 index 000000000..57ff7440b --- /dev/null +++ b/stages/005-implement@1/prompt.md @@ -0,0 +1,413 @@ +Goal: --- +title: "refactor: Remove IP allowlisting" +type: refactor +status: active +date: 2026-05-27 +--- + +# refactor: Remove IP allowlisting + +## Overview + +Remove Fabro's inbound server IP allowlisting feature completely. This removes the +server-wide `[server.ip_allowlist]` setting, the GitHub webhook-specific +`[server.integrations.github.webhooks.ip_allowlist]` overlay, request middleware, +client-IP extraction, GitHub `/meta` hook-range expansion, settings API exposure, +generated API client types, and the Settings > Security UI display. + +Existing configs that still contain removed IP allowlist keys should fail as +unknown fields. This is an intentional hard removal, not a compatibility +deprecation. + +## Problem Frame + +The feature is being removed from Fabro rather than maintained as an in-process +network access-control layer. Network source restrictions should be handled +outside Fabro by reverse proxies, firewalls, VPNs, Tailscale, platform ingress, +or other deployment-layer controls. + +## Requirements Trace + +- R1. Remove all runtime enforcement of inbound source-IP allowlisting from web, + API, static asset, and GitHub webhook routes. +- R2. Remove the server settings schema for global and GitHub webhook IP + allowlists. +- R3. Remove `IpAllowEntry` and related API schema/client types from public + settings payloads. +- R4. Keep unrelated allowlists intact: GitHub username allowlists, sandbox + egress CIDR allow lists, and internal policy/test allowlists. +- R5. Preserve existing authentication, GitHub webhook HMAC verification, + routing, health checks, request logging, and settings hot-reload behavior. +- R6. Treat old IP allowlist config as invalid after removal. +- R7. Update operator-facing docs/changelog so users know to move source-IP + restrictions upstream. + +## Scope Boundaries + +- Do not remove `[server.auth.github].allowed_usernames`. +- Do not remove Daytona/sandbox `cidr_allow_list` network policy support. +- Do not remove generic uses of "allowlist" in policy tests, markdown rendering, + model controls, or other unrelated domains. +- Do not add a migration, warning-only parser, or fallback compatibility path for + old IP allowlist settings. +- Do not change GitHub webhook signature verification or webhook route strategy. + +## Context & Research + +### Relevant Code and Patterns + +- Settings use sparse config layers in `lib/crates/fabro-config/src/layers/` + and dense resolved types in `lib/crates/fabro-types/src/settings/`. +- The OpenAPI spec at `docs/public/api-reference/fabro-api.yaml` is the source + of truth for API wire shape; `cargo build -p fabro-api` regenerates Rust API + code, and `cd lib/packages/fabro-api-client && bun run generate` regenerates + the TypeScript client. +- `lib/crates/fabro-server/src/server.rs` builds the router and currently wires + IP allowlist middleware before other route dispatch behavior. +- `lib/crates/fabro-server/src/serve.rs` currently resolves server and webhook + allowlist configs at startup and creates a GitHub `/meta` resolver. +- `apps/fabro-web/app/routes/settings-security.tsx` displays the settings API's + `server.ip_allowlist` state. + +### Main Removal Surfaces + +- Config/types: `fabro-types`, `fabro-config`, config tests. +- Server runtime: `fabro-server/src/ip_allowlist.rs`, router wiring, startup + wiring, test helpers, routing/TCP tests. +- API/contracts: OpenAPI schema, `fabro-api` type replacements/exports, generated + TypeScript API client models. +- Frontend/docs: Settings > Security copy, settings navigation copy, changelog. + +## Key Technical Decisions + +- **Hard removal:** Removed config keys should fail through existing + `deny_unknown_fields` behavior. This keeps the change simple and makes stale + deployment config visible immediately. +- **Delete, do not stub:** Remove `IpAllowlistConfig` and middleware parameters + instead of passing default empty configs through the router. Empty stubs would + keep the feature shape alive and make future cleanup harder. +- **Keep webhook auth unchanged:** GitHub webhook source-IP filtering goes away, + but HMAC signature verification remains the security boundary for webhook + payload authenticity. +- **Generated clients follow OpenAPI:** Update the OpenAPI schemas first, then + regenerate Rust and TypeScript API outputs rather than hand-editing generated + client files except as a short-term cleanup if generation leaves stale exports. +- **Docs mention upstream controls:** The changelog and security guidance should + direct operators to network-layer controls, not a replacement Fabro setting. + +## Implementation Units + +- [ ] **Unit 1: Remove config schema and resolved types** + +**Goal:** Delete the IP allowlist settings contract from config parsing and dense +server settings. + +**Requirements:** R2, R3, R4, R6 + +**Dependencies:** None + +**Files:** +- Modify: `lib/crates/fabro-types/src/settings/server.rs` +- Modify: `lib/crates/fabro-types/src/settings/mod.rs` +- Modify: `lib/crates/fabro-types/Cargo.toml` +- Modify: `lib/crates/fabro-config/src/layers/server.rs` +- Modify: `lib/crates/fabro-config/src/layers/mod.rs` +- Modify: `lib/crates/fabro-config/src/lib.rs` +- Modify: `lib/crates/fabro-config/src/resolve/server.rs` +- Modify: `lib/crates/fabro-config/src/tests/resolve_server.rs` + +**Approach:** +- Remove `ServerNamespace.ip_allowlist` and its `test_default()` population. +- Delete `ServerIpAllowlistSettings`, `ServerIpAllowlistOverrideSettings`, and + `IpAllowEntry`. +- Remove `ServerLayer.ip_allowlist`, `ServerIpAllowlistLayer`, + `ServerIpAllowlistOverrideLayer`, and `IntegrationWebhooksLayer.ip_allowlist`. +- Remove resolver functions and validation for global and webhook IP allowlists, + including `github_meta_hooks` parsing and Unix socket trusted-proxy checks. +- Remove `ipnet` from `fabro-types`. Keep `ipnet` in `fabro-config` because + `resolve/environment.rs` still validates sandbox CIDR policy. +- Replace old positive/negative IP allowlist config tests with unknown-field + tests for `[server.ip_allowlist]` and + `[server.integrations.github.webhooks.ip_allowlist]`. + +**Patterns to follow:** +- Existing unknown-field tests in `lib/crates/fabro-config/src/tests/resolve_server.rs` + for retired server settings. +- Existing `ServerLayer`/`ServerNamespace` layer-to-resolved pattern for removing + a server subdomain cleanly. + +**Test scenarios:** +- Error path: TOML with `[server.ip_allowlist]` fails parsing/resolution with an + unknown-field diagnostic mentioning `ip_allowlist`. +- Error path: TOML with `[server.integrations.github.webhooks.ip_allowlist]` + fails with an unknown-field diagnostic mentioning `ip_allowlist`. +- Happy path: minimal valid server settings still resolve without any + `ip_allowlist` field. +- Integration: serialized `ServerSettings` JSON no longer includes + `server.ip_allowlist`. + +**Verification:** +- `fabro-config` and `fabro-types` compile without removed IP allowlist symbols. +- Config tests prove stale allowlist settings are rejected. + +- [ ] **Unit 2: Remove server middleware and startup resolution** + +**Goal:** Remove all runtime source-IP filtering and GitHub `/meta` range +resolution from `fabro-server`. + +**Requirements:** R1, R4, R5 + +**Dependencies:** Unit 1 + +**Files:** +- Delete: `lib/crates/fabro-server/src/ip_allowlist.rs` +- Modify: `lib/crates/fabro-server/src/lib.rs` +- Modify: `lib/crates/fabro-server/src/server.rs` +- Modify: `lib/crates/fabro-server/src/serve.rs` +- Modify: `lib/crates/fabro-server/src/test_support.rs` +- Modify: `lib/crates/fabro-server/Cargo.toml` +- Modify: `lib/crates/fabro-server/src/auth/translate.rs` +- Modify: `lib/crates/fabro-server/src/web_auth.rs` +- Modify: `lib/crates/fabro-cli/tests/it/support/auth_harness.rs` + +**Approach:** +- Remove the public `ip_allowlist` module export. +- Delete `IpAllowlistConfig`, `IpAllowlist`, `GitHubMetaResolver`, client-IP + extraction, middleware, and GitHub meta cache helpers. +- Simplify `build_router_with_options` by removing its `Arc` + parameter and removing `RouterOptions.github_webhook_ip_allowlist`. +- Remove the global allowlist middleware layer from the main app router. +- Simplify `github_webhook_routes` so it only receives the webhook secret and no + route-specific allowlist config. +- Remove startup creation of `GitHubMetaResolver`, `default_ip_allowlist`, + `resolve_github_webhook_ip_allowlist`, and + `resolve_startup_github_webhook_ip_allowlist`. +- Replace all test/helper call sites that pass `IpAllowlistConfig::default()` + with the simplified router signature. +- Remove `ipnet` and any now-unused HTTP mocking/test-only dependencies from + `fabro-server` if they are only used by the deleted module. +- Consider whether `serve.rs` still needs `SocketAddr` for TCP + `ConnectInfo`; if it was only present for allowlisting tests, remove the + connect-info service wrapper and imports. + +**Patterns to follow:** +- Keep router layers ordered as they are after the allowlist layer is removed: + auth translation, demo routing, auth extension, canonical host, security + headers, HTTP logging, request ID. +- Existing webhook route HMAC tests and auth tests should remain the source of + truth for webhook behavior. + +**Test scenarios:** +- Happy path: API requests from any TCP remote address route normally when auth + requirements are satisfied. +- Happy path: `/health` remains accessible. +- Integration: GitHub webhook route still rejects missing/invalid signatures and + accepts valid signatures exactly as before. +- Cleanup: there are no references to `IpAllowlistConfig`, `ip_allowlist_middleware`, + `GitHubMetaResolver`, `github_meta_hooks`, or `github-meta-hooks.json`. + +**Verification:** +- `fabro-server` and CLI auth harness tests compile against the simplified router + API. +- Runtime startup no longer performs any GitHub `/meta` fetch for webhook IP + ranges. + +- [ ] **Unit 3: Update OpenAPI and generated API clients** + +**Goal:** Remove IP allowlist fields and schemas from public settings API +contracts and regenerated clients. + +**Requirements:** R3, R4 + +**Dependencies:** Units 1 and 2 + +**Files:** +- Modify: `docs/public/api-reference/fabro-api.yaml` +- Modify: `lib/crates/fabro-api/build.rs` +- Modify: `lib/crates/fabro-api/src/lib.rs` +- Modify: `lib/crates/fabro-api/tests/server_settings_round_trip.rs` +- Modify/generated: `lib/packages/fabro-api-client/src/models/*` + +**Approach:** +- Remove `ip_allowlist` from `ServerNamespace.required` and + `ServerNamespace.properties`. +- Remove `ServerIpAllowlistSettings`, + `ServerIpAllowlistOverrideSettings`, `IpAllowEntry`, + `LiteralIpAllowEntry`, and `GitHubMetaHooksEntry` schemas. +- Remove `IntegrationWebhooksSettings.ip_allowlist` from required/properties. +- Remove `with_replacement` entries and public re-exports for removed types in + `fabro-api`. +- Regenerate Rust API code by building `fabro-api`. +- Regenerate TypeScript Axios client and ensure stale allowlist model files and + index exports are gone. +- Strengthen round-trip tests to assert settings JSON omits + `server.ip_allowlist` and webhook `ip_allowlist`. + +**Patterns to follow:** +- Existing OpenAPI-first workflow in `AGENTS.md`. +- Existing `server_settings_family_reuses_domain_types` assertions for shared + domain type identity. + +**Test scenarios:** +- Contract: `ServerSettings` round-trips through API types without any IP + allowlist field. +- Contract: OpenAPI-generated TypeScript `ServerNamespace` has no + `ip_allowlist` property. +- Contract: `IntegrationWebhooksSettings` has only webhook strategy fields after + removal. + +**Verification:** +- Generated Rust and TypeScript clients match the updated OpenAPI spec. +- `rg "ServerIpAllowlist|IpAllowEntry|server-ip-allowlist|literal-ip-allow-entry"` + finds no remaining generated/public API references. + +- [ ] **Unit 4: Update web settings UI** + +**Goal:** Remove IP allowlist display from Settings > Security and align copy +with the new settings shape. + +**Requirements:** R3, R7 + +**Dependencies:** Unit 3 + +**Files:** +- Modify: `apps/fabro-web/app/routes/settings-security.tsx` +- Modify: `apps/fabro-web/app/routes/settings.tsx` + +**Approach:** +- Change the Security page description from "Authentication methods and network + allowlist" to authentication-only wording. +- Remove destructuring and rendering of `settings.server.ip_allowlist`. +- Remove the unused `Count` and `plural` imports if no longer used. +- Update the Settings nav description for Security from "Authentication and + network allowlist" to authentication-focused copy. + +**Patterns to follow:** +- Existing `settings-panel` row layout for Auth methods and Allowed usernames. + +**Test scenarios:** +- Typecheck: the route compiles against the regenerated API client with no + `server.ip_allowlist` property. +- UI behavior: Security page still renders auth methods and allowed usernames. +- Cleanup: no frontend references to `ip_allowlist`, `IP allowlist`, or + `trusted_proxy_count` remain. + +**Verification:** +- `apps/fabro-web` typecheck passes against the new generated client. + +- [ ] **Unit 5: Update tests, docs, changelog, and dependency lockfile** + +**Goal:** Remove stale references and document the operator-facing behavior +change. + +**Requirements:** R4, R6, R7 + +**Dependencies:** Units 1 through 4 + +**Files:** +- Modify: `lib/crates/fabro-server/tests/it/api/routing.rs` +- Modify: `lib/crates/fabro-server/tests/it/api/tcp.rs` +- Modify: `lib/crates/fabro-server/tests/it/api/settings.rs` +- Modify: `docs/public/administration/security.mdx` +- Create or modify: `docs/public/changelog/2026-05-27.mdx` +- Modify: `Cargo.lock` if dependency graph changes + +**Approach:** +- Delete route/TCP tests that only prove IP allowlist enforcement or + `ConnectInfo` behavior. +- Adjust any router setup helpers after the signature simplification. +- Add a settings API assertion that `server.ip_allowlist` is absent from + `/api/v1/settings`. +- Update security docs to explicitly state that Fabro does not provide inbound + source-IP allowlisting and operators should use upstream network controls. +- Add a changelog entry dated 2026-05-27 describing the hard removal and the + expected replacement at the deployment layer. +- Run dependency resolution after removing direct `ipnet`/test dependencies; + keep transitive or unrelated `ipnet` entries needed by sandbox CIDR validation. + +**Patterns to follow:** +- Existing changelog style in `docs/public/changelog/2026-05-26.mdx`. +- Existing settings API integration test style in + `lib/crates/fabro-server/tests/it/api/settings.rs`. + +**Test scenarios:** +- Settings API: response contains server auth, listen, storage, scheduler, and + integrations fields, but not `server.ip_allowlist`. +- Config compatibility: old IP allowlist TOML fails before startup rather than + being silently ignored. +- Docs validation: security docs no longer imply Fabro can restrict inbound + source IPs internally. + +**Verification:** +- `rg -n "ip_allowlist|trusted_proxy_count|github_meta_hooks|IP allowlist|ip allowlist"` + returns only historical archived plan/brainstorm/spec references or unrelated + non-server allowlist text. + +## System-Wide Impact + +- **Public API:** `GET /api/v1/settings` response shape changes by removing + `server.ip_allowlist` and webhook `ip_allowlist`. +- **Config compatibility:** Existing `settings.toml` files containing the removed + keys become invalid. This is intentional. +- **Runtime security posture:** Fabro no longer blocks requests based on source + IP. Operators must enforce network source restrictions upstream. +- **Webhook handling:** GitHub webhook HMAC verification remains unchanged; only + optional source-IP filtering is removed. +- **Generated clients:** Downstream TypeScript/Rust consumers that read + `server.ip_allowlist` must update. + +## Risks & Mitigations + +| Risk | Mitigation | +|------|------------| +| Operators accidentally expose a server that previously relied on Fabro IP allowlisting | Changelog and security docs explicitly call out the removal and direct operators to upstream controls. | +| Generated API clients retain stale types | Update OpenAPI first, regenerate both Rust and TypeScript clients, and run search checks for stale symbols. | +| Unrelated allowlist functionality is removed by broad search/replace | Scope searches to `IpAllow`, `ip_allowlist`, `trusted_proxy_count`, and `github_meta_hooks`; preserve sandbox CIDR and GitHub username allowlists. | +| Router signature cleanup breaks many tests | Update shared test helpers first, then compile-driven cleanup of remaining call sites. | +| `ipnet` is removed where still needed | Keep `fabro-config` dependency if environment CIDR validation still imports `ipnet::IpNet`. | + +## Test Plan + +- `cargo build -p fabro-api` +- `cd lib/packages/fabro-api-client && bun run generate` +- `cargo nextest run -p fabro-config -p fabro-types -p fabro-api -p fabro-server` +- `cd apps/fabro-web && bun run typecheck` +- `cd apps/fabro-web && bun test` +- `cargo +nightly-2026-04-14 fmt --check --all` +- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings` + +## Assumptions + +- The accepted compatibility policy is hard removal: no warning-only parser, no + migration, and no custom compatibility error path. +- Public API consumers can tolerate a breaking settings shape change for this + removed feature. +- Historical documents in `docs/plans/`, `docs/brainstorms/`, and + `docs/superpowers/` are archival and do not need rewriting. + +## Sources & References + +- Previous feature requirements: + `docs/brainstorms/2026-04-15-ip-whitelist-requirements.md` +- Previous implementation plan: + `docs/plans/2026-04-15-002-feat-ip-allowlist-plan.md` +- API workflow guidance: `AGENTS.md` +- OpenAPI source of truth: `docs/public/api-reference/fabro-api.yaml` + + +## Completed stages +- **toolchain**: succeeded + - Script: `command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1` + - Output: + ``` + cargo 1.95.0 (f2d3ce0bd 2026-03-21) + ``` +- **preflight_compile**: succeeded + - Script: `cargo check -q --workspace 2>&1` + - Output: (empty) +- **preflight_lint**: succeeded + - Script: `cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1` + - Output: (empty) + + +Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD. \ No newline at end of file diff --git a/stages/005-implement@1/provider_used.json b/stages/005-implement@1/provider_used.json new file mode 100644 index 000000000..c57772db6 --- /dev/null +++ b/stages/005-implement@1/provider_used.json @@ -0,0 +1,6 @@ +{ + "mode": "agent", + "provider": "openai", + "model": "gpt-5.5", + "reasoning_effort": "xhigh" +} \ No newline at end of file diff --git a/stages/005-implement@1/response.md b/stages/005-implement@1/response.md new file mode 100644 index 000000000..366a4080a --- /dev/null +++ b/stages/005-implement@1/response.md @@ -0,0 +1,42 @@ +Implemented the full IP allowlisting removal refactor. + +Summary: +- Removed inbound source-IP allowlist config from dense settings and sparse config layers: + - `[server.ip_allowlist]` + - `[server.integrations.github.webhooks.ip_allowlist]` + - `IpAllowEntry` and related settings/types. +- Deleted server runtime enforcement: + - Removed `ip_allowlist` middleware/module. + - Removed `IpAllowlistConfig`, client-IP extraction, trusted proxy handling, and GitHub `/meta` hook range resolution. + - Simplified router/startup wiring and removed `ConnectInfo` serving requirements that only existed for allowlisting. +- Preserved unrelated allowlists: + - GitHub username allowlists remain. + - Sandbox/Daytona CIDR allow lists remain. +- Updated OpenAPI and generated clients: + - Removed allowlist schemas/fields from `fabro-api.yaml`. + - Regenerated Rust API and TypeScript API client. + - Deleted stale generated TS allowlist model files. +- Updated web UI: + - Removed Settings > Security network allowlist display. + - Adjusted Security page/nav copy. +- Updated tests/docs: + - Added unknown-field tests proving stale IP allowlist config is now rejected. + - Added settings API assertions that removed fields are absent. + - Updated security docs and changelog to direct operators to upstream network controls. +- Updated dependency graph: + - Removed direct `ipnet` deps from `fabro-types` and `fabro-server`; retained transitive/use in `fabro-config` for sandbox CIDR validation. + +Red/green TDD: +- Added stale-config rejection tests and verified they failed before schema removal, then passed after removal. + +Validation run: +- `cargo build -p fabro-api` +- `cd lib/packages/fabro-api-client && bun run generate` +- `cargo build -p fabro-cli --bin fabro` +- `cargo nextest run -p fabro-config -p fabro-types -p fabro-api -p fabro-server --no-fail-fast` +- `cargo +nightly-2026-04-14 fmt --check --all` +- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings` +- `git diff --check` +- Stale-symbol search confirmed remaining `ip_allowlist`/IP allowlist references are only intentional docs/tests/assertions. + +No commits were created. \ No newline at end of file