Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
a21efc2
feat: harden chat completions request validation
mouse-value-add Apr 8, 2026
1d5e025
feat: add panic recovery middleware for HTTP server
mouse-value-add Apr 9, 2026
3f49095
fix: ensure SSE streams always terminate with [DONE]
mouse-value-add Apr 13, 2026
ba37de6
fix: upsert OpenAI base URL env vars when wrapping commands
mouse-value-add Apr 13, 2026
ef8bcfa
fix: cap upstream provider error body reads
mouse-value-add Apr 14, 2026
c6e0dcb
fix(router): route only to models available from registered providers
mouse-value-add Apr 14, 2026
e13757c
Merge pull request #17 from mouse-value-add/chore/frugal-afternoon-20…
brainsparker Apr 14, 2026
51fef7c
feat: harden provider HTTP clients with defensive transport timeouts
mouse-value-add Apr 15, 2026
fcfde96
fix(dx): normalize X-Frugal-Quality parsing
mouse-value-add Apr 15, 2026
125e6d2
Merge pull request #19 from mouse-value-add/chore/frugal-morning-2026…
brainsparker Apr 15, 2026
209f297
fix(router): default unknown quality thresholds to balanced
mouse-value-add Apr 15, 2026
87df773
Merge pull request #20 from mouse-value-add/chore/frugal-afternoon-20…
brainsparker Apr 15, 2026
8f8ad6d
fix(config): reject unknown YAML fields during load
mouse-value-add Apr 16, 2026
0b1ba8d
Merge pull request #22 from mouse-value-add/chore/frugal-morning-2026…
brainsparker Apr 16, 2026
90c2e61
fix(proxy): cap fallback attempts to bound latency and spend
mouse-value-add Apr 17, 2026
1500bdc
Merge pull request #24 from mouse-value-add/chore/frugal-nightly-hard…
brainsparker Apr 17, 2026
df750d4
fix(streaming): raise SSE scanner buffer to avoid large-chunk failures
mouse-value-add Apr 17, 2026
9fb4148
Merge pull request #25 from mouse-value-add/chore/frugal-morning-2026…
brainsparker Apr 17, 2026
9c93f35
fix(proxy): dedupe fallback chain and skip selected model retries
mouse-value-add Apr 18, 2026
491cf9a
fix(classifier): detect coding and math keywords case-insensitively
mouse-value-add Apr 18, 2026
759b6ec
feat(server): add hardened HTTP timeouts with env overrides
mouse-value-add Apr 19, 2026
13210bd
Merge pull request #30 from mouse-value-add/chore/frugal-nightly-hard…
brainsparker Apr 19, 2026
2db65bc
chore: relicense under BUSL 1.1
brainsparker Apr 19, 2026
835a9ee
test(provider): cover SSE scanner size bounds
mouse-value-add Apr 19, 2026
e0b569a
hardening: cap HTTP header bytes with validated env override
mouse-value-add Apr 19, 2026
b319f88
Merge pull request #32 from mouse-value-add/chore/frugal-afternoon-20…
brainsparker Apr 19, 2026
04adc62
Merge pull request #35 from brainsparker/chore/busl-license-2026-04-19
brainsparker Apr 20, 2026
7741a04
Merge pull request #1 from mouse-value-add/chore/nightly-hardening-20…
brainsparker Apr 20, 2026
d4c938d
Merge pull request #2 from mouse-value-add/chore/nightly-hardening-20…
brainsparker Apr 20, 2026
31294bd
Merge pull request #12 from mouse-value-add/chore/frugal-nightly-hard…
brainsparker Apr 20, 2026
3deabaf
Merge pull request #13 from mouse-value-add/chore/frugal-morning-2026…
brainsparker Apr 20, 2026
be675ad
Merge pull request #15 from mouse-value-add/chore/frugal-nightly-hard…
brainsparker Apr 20, 2026
73c2a06
Merge pull request #18 from mouse-value-add/chore/frugal-nightly-hard…
brainsparker Apr 20, 2026
0a5bd23
Merge pull request #27 from mouse-value-add/chore/frugal-nightly-hard…
brainsparker Apr 20, 2026
819c9c7
Merge pull request #29 from mouse-value-add/chore/frugal-afternoon-20…
brainsparker Apr 20, 2026
b26479f
Merge pull request #31 from mouse-value-add/chore/frugal-morning-2026…
brainsparker Apr 20, 2026
00f9b3e
docs: revise GTM plan and supersede TPRD monetization sections
brainsparker Apr 20, 2026
afa0612
feat(eval): add simulation-only eval harness
brainsparker Apr 20, 2026
3fb6bf6
Merge pull request #36 from brainsparker/chore/gtm-revision-eval-harness
brainsparker Apr 20, 2026
3ddf626
fix(core): restore OpenAI SDK compatibility, fix multimodal + Gemini …
brainsparker Apr 21, 2026
3207909
feat(security): auth, loopback default, rate limit, sanitized errors,…
brainsparker Apr 21, 2026
a27c880
feat(observability): slog, request IDs, metrics, retries, graceful li…
brainsparker Apr 21, 2026
3712bb9
feat(credibility): scored provenance, classifier tightening, protocol…
brainsparker Apr 21, 2026
4c4d090
Merge pull request #37 from brainsparker/chore/hardening-phase-4-cred…
brainsparker Apr 21, 2026
13ccf58
docs: add frugal.sh landing page (static, Cloudflare-Pages ready)
brainsparker Apr 21, 2026
af5e559
fix(install): point REPO at the real repo slug
brainsparker Apr 21, 2026
7ce6941
feat(bench): turn "40–70% savings" into a reproducible measurement
brainsparker Apr 21, 2026
1eda963
chore(deploy): pin wrangler.jsonc for Cloudflare Workers Assets
brainsparker Apr 21, 2026
520a41e
chore(deploy): drop wrangler.jsonc, switching to Cloudflare Pages
brainsparker Apr 21, 2026
5f67365
feat(usecase): route chat by use case via X-Frugal-Use-Case (Ring 1a)
brainsparker Apr 21, 2026
7f16650
new page
brainsparker Apr 22, 2026
ff1d3c7
Merge pull request #38 from brainsparker/feat/usecase-routing-ring-1a
brainsparker Apr 22, 2026
d3d4943
headline
brainsparker Apr 22, 2026
8ad7f93
Merge branch 'main' of https://github.com/brainsparker/frugal
brainsparker Apr 22, 2026
48e368e
ga
brainsparker Apr 23, 2026
292f094
redirect
brainsparker Apr 23, 2026
fa752c9
feat(install): harden installer
brainsparker Apr 23, 2026
301cb55
chore(oss): public-launch readiness — README, CoC, issue/PR templates
brainsparker Apr 23, 2026
8c5507f
ci(install): add post-release smoke-test workflow
brainsparker Apr 23, 2026
579b279
fix: resolve staticcheck findings surfaced on first public CI run
brainsparker Apr 23, 2026
79a0a0f
fix(install): pipe to bash, auth GitHub API to dodge CI rate limits
brainsparker Apr 23, 2026
219d41a
chore: bump Go toolchain to 1.24 for stdlib CVE fixes
brainsparker Apr 23, 2026
009a909
chore: bump Go toolchain to 1.25 for crypto/x509 CVE
brainsparker Apr 23, 2026
8b4ea2b
fix(install,release): two bugs surfaced by first-user smoke test
brainsparker Apr 23, 2026
eaf900e
docs: accurate status tags — only chat ships today, toolchain is next
brainsparker Apr 24, 2026
ca0a513
docs(landing): drop hero capability pill row, keep trust pills
brainsparker Apr 24, 2026
edc0749
chore(deploy): nudge Cloudflare Pages to build latest main
brainsparker Apr 24, 2026
b96f0fe
chore(deploy): retrigger Cloudflare Pages build after GitHub App reauth
brainsparker Apr 24, 2026
a67d75e
fix: harden config loader with semantic validation
mouse-value-add Apr 24, 2026
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
53 changes: 53 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Bug report
description: Something is broken, crashing, or returning the wrong result
labels: ["bug"]
body:
- type: textarea
id: what-happened
attributes:
label: What happened
description: What did you do, what did you expect, what did you get instead?
placeholder: |
1. I ran `frugal serve`
2. I sent a chat completion with `X-Frugal-Use-Case: research-synthesis`
3. I expected routing to Claude Sonnet 4, got GPT-4o-mini
validations:
required: true

- type: input
id: version
attributes:
label: frugal version
description: Output of `frugal --version`
placeholder: "v0.0.1"
validations:
required: true

- type: dropdown
id: os
attributes:
label: OS
options:
- macOS (Apple silicon)
- macOS (Intel)
- Linux (amd64)
- Linux (arm64)
- Other (describe below)
validations:
required: true

- type: textarea
id: logs
attributes:
label: Relevant logs or explain output
description: |
Run with `FRUGAL_LOG_LEVEL=debug` and/or hit `/v1/routing/explain` after
the failing request. Paste relevant lines here. Redact any API keys.
render: text

- type: textarea
id: config
attributes:
label: Config (optional)
description: Any non-default env vars or edits to `~/.frugal/config/models.yaml`
render: text
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Security vulnerability
url: https://github.com/brainsparker/frugal/security/advisories/new
about: Report security issues privately via GitHub Security Advisories. See SECURITY.md for the full disclosure policy.
- name: Question or discussion
url: https://github.com/brainsparker/frugal/discussions
about: For open-ended questions, design discussions, or "how do I…" — use Discussions, not Issues.
35 changes: 35 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Feature request
description: Suggest a new capability, use case, provider, or quality-of-life improvement
labels: ["enhancement"]
body:
- type: textarea
id: problem
attributes:
label: The problem
description: What can't you do today, or what's harder than it should be?
validations:
required: true

- type: textarea
id: proposal
attributes:
label: Proposed direction
description: |
Rough shape of the solution. Bullet points are fine. Concrete is better
than abstract — API shape, CLI flag, config key, etc.
validations:
required: true

- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: What workarounds exist today? Why are they insufficient?

- type: textarea
id: context
attributes:
label: Context
description: |
Anything else worth knowing — use case, prior art in other tools,
whether you'd be open to contributing a PR.
28 changes: 28 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!--
Title format: type(scope): summary
e.g. feat(usecase): route chat by use case via X-Frugal-Use-Case
fix(install): serve install.sh from docs/
chore(deploy): pin wrangler.jsonc for Cloudflare Workers Assets
-->

## What

<!-- One or two sentences. What changed? -->

## Why

<!-- The motivating problem or the user-visible benefit. Link issues or
prior discussion if relevant. -->

## How to verify

<!--
- [ ] `make test` passes
- [ ] Manually ran ...
- [ ] Hit endpoint X and got Y
-->

## Notes for reviewers

<!-- Anything non-obvious: tradeoffs considered, alternatives rejected,
follow-ups this PR explicitly doesn't do. Optional. -->
51 changes: 51 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: "1.25"
cache: true

- name: Build
run: go build ./...

- name: Vet
run: go vet ./...

- name: Test
run: go test -race ./...

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: "1.25"
cache: true

- name: Install staticcheck
run: go install honnef.co/go/tools/cmd/staticcheck@latest

- name: Staticcheck
run: staticcheck ./...

- name: Install govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest

- name: Govulncheck
run: govulncheck ./...
16 changes: 16 additions & 0 deletions .github/workflows/dco.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: DCO

on:
pull_request:
types: [opened, synchronize, reopened]

permissions:
contents: read
pull-requests: write

jobs:
dco:
runs-on: ubuntu-latest
steps:
- name: Check Developer Certificate of Origin sign-off
uses: tim-actions/[email protected]
110 changes: 110 additions & 0 deletions .github/workflows/install-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: install-smoke

# Runs the real, public installer end-to-end on fresh runners after every
# release. Finds the "works on my Mac" class of regressions before a user does.
#
# Also runs on workflow_dispatch so you can re-test without cutting a release.
on:
release:
types: [published]
workflow_dispatch:

permissions:
contents: read

jobs:
curl-pipe-sh:
name: ${{ matrix.os }} / curl | bash
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install via public URL
env:
FRUGAL_YES: "1"
# Authenticate the releases/latest API call so shared runner IPs
# don't 403 against the 60/hr anonymous rate limit.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
curl -fsSL https://frugal.sh/install | bash

- name: Binary runs and reports a version
run: |
set -euo pipefail
"$HOME/.frugal/bin/frugal" --version

- name: Shell rc block was written
run: |
set -euo pipefail
found=0
for rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile"; do
if [ -f "$rc" ] && grep -qxF "# >>> frugal.sh >>>" "$rc"; then
echo "marker block present in $rc"
found=1
break
fi
done
if [ "$found" -ne 1 ]; then
echo "no shell rc got the frugal.sh marker block" >&2
exit 1
fi

- name: Uninstall is clean
run: |
set -euo pipefail
curl -fsSL https://frugal.sh/install | bash -s uninstall
if [ -d "$HOME/.frugal" ]; then
echo "$HOME/.frugal still exists after uninstall" >&2
exit 1
fi
for rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile"; do
if [ -f "$rc" ] && grep -qxF "# >>> frugal.sh >>>" "$rc"; then
echo "marker block still in $rc after uninstall" >&2
exit 1
fi
done

pinned-version:
name: ${{ matrix.os }} / FRUGAL_VERSION pin
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Derive tag
id: tag
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "release" ]; then
echo "value=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
else
# Manual re-runs pin to whatever is currently latest.
latest="$(curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" \
https://api.github.com/repos/${{ github.repository }}/releases/latest \
| grep '"tag_name"' | head -n1 | sed -E 's/.*"tag_name"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')"
echo "value=$latest" >> "$GITHUB_OUTPUT"
fi

- name: Install pinned version
env:
FRUGAL_YES: "1"
FRUGAL_VERSION: ${{ steps.tag.outputs.value }}
run: |
curl -fsSL https://frugal.sh/install | bash

- name: Binary reports the pinned version
run: |
set -euo pipefail
got="$("$HOME/.frugal/bin/frugal" --version)"
want="${{ steps.tag.outputs.value }}"
echo "got: $got"
echo "want: $want"
case "$got" in
*"$want"*) echo "version matches" ;;
*) echo "version mismatch" >&2; exit 1 ;;
esac
21 changes: 20 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:

permissions:
contents: write
id-token: write # required for keyless cosign via GitHub OIDC

jobs:
release:
Expand All @@ -16,14 +17,32 @@ jobs:

- uses: actions/setup-go@v5
with:
go-version: "1.23"
go-version: "1.25"

- name: Run tests
run: go test ./...

- name: Build release binaries
run: make release

- name: Install cosign
uses: sigstore/cosign-installer@v3

- name: Sign binaries and checksums (keyless)
run: |
set -euo pipefail
cd dist
for f in frugal-* SHA256SUMS; do
cosign sign-blob --yes --bundle "${f}.sig" "${f}"
done

- name: Generate SBOMs (CycloneDX)
uses: anchore/sbom-action@v0
with:
path: .
format: cyclonedx-json
output-file: dist/frugal.cdx.json

- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
Expand Down
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Binaries
/bin/
/dist/

# Go build outputs and local envs
/frugal
*.test

# Wrangler (Cloudflare Workers Assets) local state.
# The committed wrangler.jsonc is the source of truth; everything below is
# generated per-machine or per-CI-run.
.wrangler/
.dev.vars
wrangler-*.log
node_modules/
9 changes: 9 additions & 0 deletions ADOPTERS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Adopters

Teams and individuals running Frugal in production. If that's you and you're willing to be listed, open a PR adding yourself.

| Organization / Handle | Use case |
|---|---|
| _(be the first)_ | |

Interested in being a design partner — running Frugal on a real workload with hands-on support in exchange for feedback — open an issue titled `design partner: <your use case>`.
Loading
Loading