Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f1022cd
feat(db/board-service): Postgres persistence
Jul 5, 2026
ffa47b6
feat(db/audit-store): Postgres persistence
Jul 5, 2026
acfa2d0
feat(db/approval-repository): Postgres persistence
Jul 5, 2026
6835076
feat(db/policy-override-grant-store): Postgres persistence
Jul 5, 2026
237285f
feat(db/api-key-backend): Postgres persistence
Jul 5, 2026
b3be6e7
feat(db/policy-audit-sink): Postgres persistence
Jul 5, 2026
04f29b1
feat(db/spec-projection-repository): Postgres persistence
Jul 5, 2026
084aa31
feat(db/pm-link-repository): Postgres persistence
Jul 5, 2026
43857d7
feat(db/secret-vault-store): Postgres persistence
Jul 5, 2026
d94202e
feat(db/idempotency-store): Postgres persistence
Jul 5, 2026
a0ca9c1
docs(db): PERSISTENCE_PROGRESS ledger for 10 Postgres-backed stores
Jul 5, 2026
6f4df98
docs: use service-hive/forge org URLs
Jul 5, 2026
4314749
style: apply ruff format across the tree (was never enforced by the b…
Jul 5, 2026
f75e877
ci: make GitHub Actions green on the private org repo
Jul 5, 2026
f766f94
fix(ci/mypy-packages): resolve mypy errors
Jul 5, 2026
dc73e16
fix(ci/mypy-worker): resolve mypy errors
Jul 7, 2026
e13085e
fix(ci/mypy-api): resolve mypy errors
Jul 7, 2026
3e8e05e
fix(ci/eslint-web): resolve eslint errors
Jul 7, 2026
d266b70
docs: use QuintinBotes/forge org URLs (repo transferred)
Jul 7, 2026
3e8fa68
ci: fix org-repo infra jobs (pnpm packageManager conflict, compose/bu…
Jul 7, 2026
bd37001
ci: drop invalid osv-scanner-action (reusable-wf, not a step) + pin h…
Jul 7, 2026
b6aeee3
ci: helm add bitnami subchart repo; waive semgrep false-positives (mi…
Jul 7, 2026
dbea801
ci: put embeddings sha1 nosemgrep on its own line (was E501, 177>100)
Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
41 changes: 27 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ jobs:
# env (never interpolated into the script body) and written with printf; no
# `set -x` around the write so it is never echoed.
- name: Materialise GitHub App private key
if: ${{ secrets.FORGE_GITHUB_APP_ID != '' }}
if: ${{ env.FORGE_GITHUB_APP_ID != '' }}
env:
APP_PRIVATE_KEY: ${{ secrets.FORGE_GITHUB_APP_PRIVATE_KEY }}
run: |
Expand Down Expand Up @@ -247,9 +247,9 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Install pnpm
# Version comes from package.json "packageManager" — passing `version:`
# too makes pnpm/action-setup v4 fail with "multiple versions specified".
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
with:
version: 10

- name: Set up Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
Expand Down Expand Up @@ -280,9 +280,11 @@ jobs:
security:
name: security (sast + deps + secrets + sbom + matrix)
runs-on: ubuntu-latest
# No security-events:write — code scanning (SARIF upload) needs GitHub
# Advanced Security, which a private repo can't grant (fails "Set up job").
# The SARIF-upload step is already guarded to skip on private repos.
permissions:
contents: read
security-events: write # upload SARIF to code scanning
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
Expand Down Expand Up @@ -318,17 +320,16 @@ jobs:
run: >-
uv export --frozen --format requirements-txt --no-emit-workspace
| uv run pip-audit -r /dev/stdin --strict
- name: osv-scanner (lockfiles)
uses: google/osv-scanner-action@764c91816374ff2d8fc2095dab36eecd42d61638 # v1.9.2
continue-on-error: true
with:
scan-args: "--lockfile=uv.lock --lockfile=apps/web/pnpm-lock.yaml"

# --- Secret scan (full history) --- #
- name: gitleaks
uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2.3.9
env:
GITLEAKS_CONFIG: .gitleaks.toml
# The gitleaks GitHub Action requires a paid license for ORGANIZATION
# repos; run the pinned binary directly instead (MIT, no license needed).
# `gitleaks detect` scans the full git history and exits non-zero on a hit.
- name: gitleaks (binary, no org license)
run: |
curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz" \
| tar -xz -C /tmp gitleaks
/tmp/gitleaks detect --source . --config .gitleaks.toml --redact --no-banner

# --- SBOM --- #
- name: SBOM (CycloneDX)
Expand All @@ -338,8 +339,12 @@ jobs:
- name: Enforcement matrix
run: uv run pytest -m security -q

# SARIF upload needs code scanning (GitHub Advanced Security), which is
# unavailable on a PRIVATE repo — skip it there so the gate stays green;
# it activates automatically once the repo is public. The SAST steps above
# still run and gate on high/critical regardless.
- name: Upload SARIF
if: always()
if: ${{ always() && github.event.repository.private == false }}
uses: github/codeql-action/upload-sarif@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
with:
sarif_file: "."
Expand Down Expand Up @@ -410,6 +415,10 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Validate production compose
# The prod compose requires ${FORGE_SECRET_KEY:?} by design; supply a
# throwaway value so `config` can validate (never a real secret).
env:
FORGE_SECRET_KEY: ci-compose-validation-dummy-not-a-secret
run: docker compose -f deploy/docker-compose.yml config --quiet

- name: Validate dev compose
Expand All @@ -424,6 +433,10 @@ jobs:
build:
name: build (images + sbom + smoke)
runs-on: ubuntu-latest
# The prod compose requires ${FORGE_SECRET_KEY:?}; `compose build` + smoke
# parse it, so provide a throwaway value (never a real secret).
env:
FORGE_SECRET_KEY: ci-build-validation-dummy-not-a-secret
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ jobs:
analyze:
name: analyze (${{ matrix.language }})
runs-on: ubuntu-latest
# Code scanning (CodeQL SARIF upload) requires GitHub Advanced Security,
# which isn't available on a PRIVATE repo — skip cleanly there so the check
# doesn't fail; this activates automatically once the repo is public.
if: ${{ github.event.repository.private == false }}
permissions:
contents: read # checkout
security-events: write # upload CodeQL results to code scanning
Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/helm-chart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ jobs:
- name: Install Helm
uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4.3.0
with:
version: v3.16.2
version: v3.17.3

- name: Install helm-unittest plugin
run: helm plugin install https://github.com/helm-unittest/helm-unittest --version 1.1.1
# Pin to the last 0.x: 1.x plugin.yaml uses `platformHooks`, which needs
# Helm >= 3.18; 0.8.2 loads on 3.16/3.17 and runs the same test format.
run: helm plugin install https://github.com/helm-unittest/helm-unittest --version 0.8.2

- name: Install kubeconform
run: |
Expand All @@ -48,6 +50,9 @@ jobs:
with:
enable-cache: true

- name: Add subchart repos (Bitnami postgres/redis)
run: helm repo add bitnami https://charts.bitnami.com/bitnami

- name: Build chart dependencies
run: helm dependency build ${CHART}

Expand Down Expand Up @@ -90,7 +95,7 @@ jobs:
- name: Install Helm
uses: azure/setup-helm@b9e51907a09c216f16ebe8536097933489208112 # v4.3.0
with:
version: v3.16.2
version: v3.17.3

- name: Create kind cluster
uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0
Expand Down
11 changes: 7 additions & 4 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@ jobs:
with:
fetch-depth: 0 # full history for gitleaks

- name: gitleaks
uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2.3.9
env:
GITLEAKS_CONFIG: .gitleaks.toml
# The gitleaks GitHub Action requires a paid license for ORGANIZATION
# repos; run the pinned binary directly instead (MIT, no license needed).
- name: gitleaks (binary, no org license)
run: |
curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz" \
| tar -xz -C /tmp gitleaks
/tmp/gitleaks detect --source . --config .gitleaks.toml --redact --no-banner

# ------------------------------------------------------------------- #
# Python SAST + dependency audit: bandit (high/critical gate) and #
Expand Down
7 changes: 7 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,11 @@ paths = [
# committed so `docker compose -f deploy/docker-compose.dev.yml up` works
# with zero setup. Waiver W-005 in security/waivers.yaml.
'''deploy/\.env\.dev''',
# Frontend walkthrough: `WALKTHROUGH_STORAGE_KEY = "forge.walkthrough.v1"` is a
# localStorage KEY NAME (a `*_KEY` constant), not a credential — false positive.
'''apps/web/src/components/walkthrough/tour-context\.tsx''',
# Fake `forge_scim_SECRET123` SCIM token in a Vitest mock (SSO settings view).
'''apps/web/src/components/sso/sso-settings-view\.test\.tsx''',
# Fake `sk-…` fixture whose test asserts the Postgres audit store REDACTS it.
'''apps/api/tests/test_audit_store_db\.py''',
]
5 changes: 5 additions & 0 deletions .semgrepignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ tests/
*/tests/*
packages/*/tests/
apps/*/tests/
conftest.py
# Alembic migrations are reviewed DDL (CREATE SCHEMA / DO $$ blocks / ALTER TABLE)
# that legitimately use sqlalchemy.text() with INTERNAL constants (table names,
# not user input) — the avoid-sqlalchemy-text rule is a false positive there.
packages/*/migrations/
docs/
examples/
spec-templates/
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
> a sandboxed agent runtime, hybrid knowledge retrieval, and a native project
> board, all on one Postgres-backed platform you run yourself.

<!-- Badge placeholders — replace <org>/forge with your repository slug before publishing. -->
<!-- Badge placeholders — replace QuintinBotes/forge with your repository slug before publishing. -->
[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./LICENSE)
[![CI](https://img.shields.io/badge/CI-see%20Actions-lightgrey.svg)](../../actions)
[![Status: pre-1.0](https://img.shields.io/badge/status-pre--1.0%20(active)-orange.svg)](#status)
Expand Down Expand Up @@ -65,7 +65,7 @@ for the honest per-feature ledger.
Requires Docker Engine 24+ and the Docker Compose v2 plugin, plus `make`.

```bash
git clone https://github.com/<org>/forge.git
git clone https://github.com/QuintinBotes/forge.git
cd forge
cp .env.example .env # then set SECRET_KEY, POSTGRES_PASSWORD, DOMAIN, ...
make dev # build + start the full stack, migrate, seed, wait healthy
Expand Down
4 changes: 1 addition & 3 deletions apps/api/forge_api/auth/apikeys.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,7 @@ def get(self, workspace_id: uuid.UUID, key_id: uuid.UUID) -> APIKeyRecord | None
class APIKeyStore:
"""Mint, verify, list, and revoke Forge API keys."""

def __init__(
self, *, secret_key: bytes, backend: APIKeyBackend | None = None
) -> None:
def __init__(self, *, secret_key: bytes, backend: APIKeyBackend | None = None) -> None:
if len(secret_key) < 16:
raise ValueError("secret_key must be at least 16 bytes")
self._secret = bytes(secret_key)
Expand Down
Loading
Loading