Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to FoundryGate should be documented here.

The format is intentionally lightweight and human-readable. Group entries by release and focus on user-visible behavior, operational changes, and compatibility notes.

## Unreleased

### Added

- Added dashboard CSP hashes plus stricter response-security defaults for the no-build operator UI
- Added stronger provider base URL validation so non-local upstreams must use `https`
- Added reduced leakage of upstream provider failure details in client-facing error payloads
- Added a separate npm CLI package under `packages/foundrygate-cli` for basic health, model, update, and route-preview checks
- Added a documented `v1.0.0` security review with mitigations and residual-risk notes
- Added functional API coverage for upstream error sanitization on top of the earlier dashboard and request-boundary hardening tests

## v0.9.0 - 2026-03-15

### Added
Expand Down
39 changes: 26 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ If every configured provider API key is empty, FoundryGate still starts, but it
- [Integrations](./docs/INTEGRATIONS.md)
- [Onboarding](./docs/ONBOARDING.md)
- [Publishing](./docs/PUBLISHING.md)
- [Security Review](./docs/SECURITY-REVIEW-v1.0.0.md)
- [Troubleshooting](./docs/TROUBLESHOOTING.md)
- [Roadmap](./docs/FOUNDRYGATE-ROADMAP.md)

Expand Down Expand Up @@ -845,6 +846,17 @@ python -m build

Tagged releases always build Python artifacts. PyPI publishing is wired behind the repository variable `PYPI_PUBLISH=true` plus GitHub trusted publishing for the `pypi` environment.

### Separate npm CLI Package

`v1.0.0` adds a separate npm-facing CLI package in [packages/foundrygate-cli](./packages/foundrygate-cli).

The package stays intentionally small and separate from the Python gateway core. It currently focuses on operator- and integration-friendly commands such as:

- `foundrygate-cli health`
- `foundrygate-cli models`
- `foundrygate-cli update`
- `foundrygate-cli route --message "..."`

## Publishing

FoundryGate now has a real publish dry-run path for both Python artifacts and the container image.
Expand Down Expand Up @@ -877,6 +889,17 @@ Running `./scripts/foundrygate-install` also creates symlinks in `/usr/local/bin
| `foundrygate-doctor` | Checks for config/env presence, writable DB path, at least one configured provider key, and optional local health endpoints |
| `foundrygate-onboarding-report` | Summarizes provider readiness, staged rollout readiness, client-profile coverage, client match intent, routing layers, onboarding suggestions, and concrete OpenClaw/n8n/CLI quickstarts |
| `foundrygate-onboarding-validate` | Exits non-zero when onboarding blockers exist and prints warnings for common multi-provider and multi-client misconfigurations |
| `foundrygate-install` | Installs the unit file, creates `/var/lib/foundrygate`, creates helper symlinks, reloads `systemd`, and starts the service |
| `foundrygate-start` | Runs `systemctl start foundrygate.service` |
| `foundrygate-stop` | Runs `systemctl stop foundrygate.service` |
| `foundrygate-restart` | Runs `systemctl restart foundrygate.service` |
| `foundrygate-status` | Shows service status and checks whether `127.0.0.1:8090` is listening |
| `foundrygate-logs` | Tails `journalctl -u foundrygate.service` |
| `foundrygate-health` | Calls `GET /health` locally with `curl` |
| `foundrygate-update-check` | Calls `GET /api/update` locally and prints the cached release-check status |
| `foundrygate-auto-update` | Evaluates the cached update status and, with `--apply`, only runs the configured update command when the release is eligible |
| `foundrygate-update` | Fetches from Git, hard-resets to `origin/main`, cleans untracked files, reinstalls the unit, restarts, and retries health checks |
| `foundrygate-uninstall` | Stops and disables the service, removes the unit file, and removes helper symlinks |

Provider starter snippets for the first rollout path live under [docs/examples](./docs/examples):

Expand All @@ -897,17 +920,6 @@ For delegated OpenClaw traffic and future AI-native app profiles, the new starte

- [openclaw-delegated-request.json](./docs/examples/openclaw-delegated-request.json)
- [client-ai-native-app-profile.yaml](./docs/examples/client-ai-native-app-profile.yaml)
| `foundrygate-install` | Installs the unit file, creates `/var/lib/foundrygate`, creates helper symlinks, reloads `systemd`, and starts the service |
| `foundrygate-start` | Runs `systemctl start foundrygate.service` |
| `foundrygate-stop` | Runs `systemctl stop foundrygate.service` |
| `foundrygate-restart` | Runs `systemctl restart foundrygate.service` |
| `foundrygate-status` | Shows service status and checks whether `127.0.0.1:8090` is listening |
| `foundrygate-logs` | Tails `journalctl -u foundrygate.service` |
| `foundrygate-health` | Calls `GET /health` locally with `curl` |
| `foundrygate-update-check` | Calls `GET /api/update` locally and prints the cached release-check status |
| `foundrygate-auto-update` | Evaluates the cached update status and, with `--apply`, only runs the configured update command when the release is eligible |
| `foundrygate-update` | Fetches from Git, hard-resets to `origin/main`, cleans untracked files, reinstalls the unit, restarts, and retries health checks |
| `foundrygate-uninstall` | Stops and disables the service, removes the unit file, and removes helper symlinks |

`foundrygate-stats --json` now also includes client/profile breakdowns alongside provider and routing summaries.

Expand Down Expand Up @@ -963,6 +975,7 @@ Security automation and review baseline:
- [CodeQL](./.github/workflows/codeql.yml) provides code scanning on `main`, pull requests, and a weekly schedule
- [Dependabot](./.github/dependabot.yml) tracks Python, GitHub Actions, and Docker dependencies
- GitHub secret scanning is already active at the repository level
- [Security Review](./docs/SECURITY-REVIEW-v1.0.0.md) captures the `v1.0.0` release-gate review and residual-risk summary

## Repo Safety And CI

Expand Down Expand Up @@ -1053,13 +1066,13 @@ Short version:
- the completed foundation already covers capability-aware routing, local worker support, client profiles, request hooks, route introspection, route traces, local worker probing, and first multi-dimensional route-fit checks
- `v0.4.x` now focuses on deeper route scoring and dashboard refinement rather than the initial hook/dashboard baseline
- `v0.5.0` is the operator distribution baseline for Docker and PyPI publishing, onboarding helpers, and release update checks
- the path to `v1.0.0` includes modality expansion, update operations, a separate npm or TypeScript CLI package, and a full security review
- `v1.0.0` is the stable gateway baseline with the separate npm CLI package and the completed security review gate

## Releases

- [CHANGELOG.md](./CHANGELOG.md) tracks notable user-facing changes
- [RELEASES.md](./RELEASES.md) describes the lightweight release process for tags and GitHub Releases
- publishing path: GitHub Releases now, Docker and PyPI in `v0.5.0`, separate npm or TypeScript CLI package by `v1.0.0`
- publishing path: GitHub Releases, Docker, and PyPI are established, and `v1.0.0` adds the separate npm CLI package under `packages/foundrygate-cli`
- GitHub Releases: [https://github.com/typelicious/FoundryGate/releases](https://github.com/typelicious/FoundryGate/releases)

## Contributing
Expand Down
4 changes: 2 additions & 2 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ The repo also includes [publish-dry-run](./.github/workflows/publish-dry-run.yml
- `v0.6.0`: modality-aware image routing becomes an explicit release line with provider inventory and image-policy guidance.
- `v0.7.0`: helper-driven update controls become a first-class release line with scoped rollout gates and verification hooks.
- `v0.8.0`: many-provider and many-client onboarding becomes copy/pasteable and validation-backed through reports, starters, and doctor checks.
- `v1.0.0`: keep GitHub Releases, Docker, and PyPI, and add a separate npm or TypeScript CLI package if the CLI surface is ready.
- `v1.0.0`: keep GitHub Releases, Docker, and PyPI, and add the separate npm CLI package under `packages/foundrygate-cli`.

The npm or TypeScript package should stay separate from the Python gateway core. It is meant for CLI-facing integrations, not for rewriting the service runtime.
The npm package stays separate from the Python gateway core. It is meant for CLI-facing integrations, not for rewriting the service runtime.

## Scheduled Deployment Examples

Expand Down
2 changes: 2 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Please report issues such as:
- dependency vulnerabilities with practical impact
- trust-boundary issues between FoundryGate and upstream or local providers

For the `v1.0.0` release gate, the reviewed findings and residual risks are summarized in [docs/SECURITY-REVIEW-v1.0.0.md](./docs/SECURITY-REVIEW-v1.0.0.md).

## Operational Guidance

To reduce risk in deployments:
Expand Down
12 changes: 10 additions & 2 deletions docs/FOUNDRYGATE-ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The foundation that used to be the near-term buildout is largely in place:

This roadmap now shifts from "rename and foundation" to "deepen the gateway plane without bloating it".

`v0.9.x` is the current release line: the focus now shifts to pre-`v1.0` hardening across request boundaries, functional API coverage, and a full documentation pass on the already-shipped routing, modality, onboarding, and ops foundation.
`v1.0.0` is the current release line: the focus now shifts from feature accretion to a stable gateway baseline, a completed security review gate, and a separate npm CLI surface for Node-facing integrations.

## Big Picture

Expand Down Expand Up @@ -266,7 +266,7 @@ Current `v0.9.x` baseline is aimed at:
Primary goals:

- declare a stable FoundryGate gateway baseline for local-first, multi-provider routing
- publish the first separate npm or TypeScript CLI package for FoundryGate-adjacent CLI usage
- publish the first separate npm CLI package for FoundryGate-adjacent CLI usage
- complete a comprehensive security review before release

The `v1.0.0` security review should explicitly cover:
Expand All @@ -279,6 +279,14 @@ The `v1.0.0` security review should explicitly cover:

`v1.0.0` should only ship after those review results are addressed or documented with a clear mitigation plan.

Current `v1.0.0` baseline is aimed at:

- dashboard CSP hardening without turning the no-build UI into a separate frontend app
- reduced leakage of upstream provider failure details in client responses
- clearer trust-boundary validation for provider base URLs
- a documented release-gate security review with explicit residual risks
- a separate npm CLI package that complements the Python gateway instead of replacing it

## Updated near-term PR sequence

The next sequence should ladder directly into the release path above:
Expand Down
2 changes: 2 additions & 0 deletions docs/INTEGRATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ For a reusable shell starter, use [examples/cli-foundrygate-env.sh](./examples/c

As with other clients, prefer token-like client tags over long free-form values so the bounded header surface remains readable in traces and operator views.

If you want a small Node-facing helper instead of shell aliases, the separate npm package lives in [packages/foundrygate-cli](../packages/foundrygate-cli).

## AI-native app clients

For future app-specific clients, keep the same OpenAI-compatible base URL and add one stable app header before creating multiple custom profiles.
Expand Down
2 changes: 2 additions & 0 deletions docs/PUBLISHING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ FoundryGate currently ships through:
- Git tags and GitHub Releases
- Python distributions (`sdist` and `wheel`)
- a GHCR container image
- a separate npm CLI package in [packages/foundrygate-cli](../packages/foundrygate-cli)

PyPI remains opt-in and only publishes when trusted publishing is configured and `PYPI_PUBLISH=true` is set at the repository level.

Expand Down Expand Up @@ -45,6 +46,7 @@ The real publish flow stays tag-driven through [release-artifacts](../.github/wo
4. let `release-artifacts` build Python distributions and the GHCR image
5. publish the GitHub Release
6. optionally allow PyPI publication through trusted publishing
7. publish the separate npm CLI package only when you are ready to version the Node-facing surface independently

## Trust Boundaries

Expand Down
82 changes: 82 additions & 0 deletions docs/SECURITY-REVIEW-v1.0.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# FoundryGate v1.0.0 Security Review

## Scope

This review covers the release-gate areas called out in the roadmap for `v1.0.0`:

- dashboard XSS and HTML/CSS injection
- request, header, and parameter injection
- dependency and unsafe-default review
- local-worker and upstream trust boundaries
- auth, secret-handling, and writable-path assumptions

## Findings And Outcomes

### 1. Dashboard XSS / HTML / CSS injection

Status: mitigated in the current runtime baseline.

- the built-in dashboard remains a static no-build page
- dynamic values are escaped before insertion into the DOM
- the dashboard now ships with a restrictive CSP using content hashes instead of `unsafe-inline`
- `X-Frame-Options: DENY` and `Referrer-Policy: no-referrer` are enabled by default

Residual risk:

- the dashboard is still intentionally simple and unauthenticated, so it should stay bound to trusted local or operator-only network surfaces

### 2. Request / header / parameter injection

Status: mitigated for the current request surface.

- routing and operator headers are normalized and length-bounded before reaching traces, metrics, or rollout logic
- request hooks stay on a sanitized input/output surface
- oversized JSON bodies are rejected before route resolution
- oversized multipart uploads are rejected before provider calls
- provider failure details are logged internally but no longer echoed back to clients verbatim

Residual risk:

- upstream providers still define their own model- and payload-level validation semantics, so operators should keep provider-specific constraints tight in config

### 3. Dependency vulnerabilities and unsafe defaults

Status: reviewed against the current shipped surface.

- the release CI covers Ruff, CodeQL, Python tests, packaging, and artifact checks
- the runtime keeps conservative defaults for cache control and response headers
- database output stays out of the repo checkout through `FOUNDRYGATE_DB_PATH`

Residual risk:

- dependency freshness remains an ongoing maintenance task, not a one-time release action

### 4. Trust boundaries for local workers and upstream providers

Status: tightened in config validation.

- public/non-local provider URLs must now use `https`
- local or private-network workers may still use `http`
- `contract: local-worker` continues to require local/private network placement

Residual risk:

- FoundryGate is still a gateway, not an auth or service-mesh product; upstream TLS trust and network placement remain operator responsibilities

### 5. Auth, secrets, and writable paths

Status: documented and partially enforced.

- provider secrets remain environment-driven
- writable state stays outside the repo by default
- repo safety checks continue to block common artifact and secret-adjacent file types

Residual risk:

- the runtime does not currently implement end-user auth; deployments should assume a trusted local or internal edge

## Release Decision

Result: acceptable for `v1.0.0`.

The current review did not uncover a blocker that requires delaying the stable release, provided deployments keep the local-first trust model and conservative defaults intact.
25 changes: 25 additions & 0 deletions foundrygate/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,30 @@ def _looks_local_base_url(base_url: str) -> bool:
return ip.is_loopback or ip.is_private or ip.is_link_local


def _validate_provider_base_url(name: str, base_url: str) -> str:
"""Validate provider base URLs against the current trust-boundary baseline."""
parsed = urlparse(base_url)
scheme = (parsed.scheme or "").strip().lower()
if scheme not in {"http", "https"}:
raise ConfigError(
"Provider "
f"'{name}' base_url must use http or https "
f"(got '{parsed.scheme or 'missing'}')"
)

if not parsed.netloc:
raise ConfigError(f"Provider '{name}' base_url must include a host")

if scheme == "http" and not _looks_local_base_url(base_url):
raise ConfigError(
"Provider "
f"'{name}' base_url must use https unless it points "
"to local/private network space"
)

return base_url


def _normalize_provider_capabilities(name: str, cfg: dict[str, Any]) -> dict[str, Any]:
"""Normalize and validate provider capability metadata."""
raw = cfg.get("capabilities") or {}
Expand Down Expand Up @@ -386,6 +410,7 @@ def _normalize_provider(name: str, cfg: Any) -> dict[str, Any]:
value = normalized.get(field, "")
if not isinstance(value, str) or not value.strip():
raise ConfigError(f"Provider '{name}' must define a non-empty '{field}'")
normalized["base_url"] = _validate_provider_base_url(name, str(normalized["base_url"]).strip())

context_window = _normalize_positive_int(
normalized.get("context_window"),
Expand Down
Loading
Loading