Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
4a57b8c
Scaffold token-metering interceptor (phoebe)
hhuuggoo Jun 4, 2026
93d301d
M1: streaming tee — forward-then-inspect SSE capture
hhuuggoo Jun 4, 2026
2465636
M4: dynamic model dispatch — ConventionResolver, CachedResolver, Chai…
hhuuggoo Jun 4, 2026
c7d1dce
M2: durable metering emitter — three-level durability ladder
hhuuggoo Jun 4, 2026
37d1b8f
Merge M2 (durable emit) into integration
hhuuggoo Jun 4, 2026
8ebf171
Merge M4 (dynamic dispatch) into integration
hhuuggoo Jun 4, 2026
c5df735
M3: streaming abort correctness — race-safe markAborted, bill-partial…
hhuuggoo Jun 4, 2026
3b625d8
Wire DurableEmitter + dynamic resolver into main.go
hhuuggoo Jun 4, 2026
dd70fde
Capture X-Saturn-Auth-Id and full identity into metering events
hhuuggoo Jun 5, 2026
ba35970
Fail closed on requests missing billing identity
hhuuggoo Jun 5, 2026
6f34727
Add DESIGN.md capturing architecture decisions and rationale
hhuuggoo Jun 7, 2026
43c914b
Add Postgres drainer: durable consumer of the metering stream
hhuuggoo Jun 7, 2026
9955df6
M5: add iolog subsystem — opt-in, sampled, off-by-default body capture
hhuuggoo Jun 7, 2026
a9ab527
Add Rating v1: turn metered token counts into money
hhuuggoo Jun 8, 2026
f026d55
Count unattributable billing_event rows instead of silently dropping
hhuuggoo Jun 8, 2026
111e9a0
Add engine-conformance harness: validate usage parsing vs a real engine
hhuuggoo Jun 8, 2026
4e96fe8
Fix lost metering event on aborted requests (abort-path race)
hhuuggoo Jun 8, 2026
b8771d4
Rating v2: NUMERIC money, SQL-side rater, model_id price key + global…
hhuuggoo Jun 8, 2026
ff93b48
Merge abort race fix
hhuuggoo Jun 8, 2026
b4b6d01
Merge M5 I/O-logging
hhuuggoo Jun 8, 2026
4fbcf4a
Merge DESIGN.md
hhuuggoo Jun 8, 2026
bec03db
Merge engine-conformance harness
hhuuggoo Jun 8, 2026
583528e
Dockerfile: build all three binaries (interceptor, drainer, rater)
hhuuggoo Jun 8, 2026
ef8e8c2
rating: move the Go cost oracle into _test.go files
hhuuggoo Jun 9, 2026
b45a31c
proxy: capture the engine-reported model name as the price key
hhuuggoo Jun 9, 2026
2b3b704
emit: rotate the WAL instead of read-then-truncate (stop losing events)
hhuuggoo Jun 9, 2026
d5dcae1
rating+iolog: TZ-safe price overlap, hour-aligned windows, fail-close…
hhuuggoo Jun 9, 2026
39e38fa
rating: make the conformance test actually pin the SQL (CI + sub-nano…
hhuuggoo Jun 9, 2026
0d28302
ci: add integration-test job (live Postgres conformance)
hhuuggoo Jun 9, 2026
369ce42
proxy: gate client-controlled X-Request-Id; fix onDone context + canc…
hhuuggoo Jun 10, 2026
ad2e5da
drain: isolate poison rows instead of wedging the batch forever
hhuuggoo Jun 10, 2026
34f8f9c
drain: bind empty model as NULL so capture gaps land in the right ano…
hhuuggoo Jun 10, 2026
0741cea
emit: replace hand-rolled WAL with tidwall/wal (sequential entries + …
hhuuggoo Jun 10, 2026
3a19334
migrations: index the rater's actual scan predicate, not bare event_ts
hhuuggoo Jun 10, 2026
172cfec
rating: one-snapshot anomaly accounting, TZ-independent buckets, dete…
hhuuggoo Jun 10, 2026
1f1fbcf
rater: default window is the trailing N complete hours (catch late dr…
hhuuggoo Jun 10, 2026
b15ad69
iolog: keep truncation on rune boundaries, sanitize bodies for PG TEX…
hhuuggoo Jun 10, 2026
2a963da
emit+interceptor: never strand an event at shutdown
hhuuggoo Jun 10, 2026
53b3c66
docs+config: WALPath is now a directory; describe the tidwall/wal design
hhuuggoo Jun 10, 2026
71f52a1
Merge WAL rework: tidwall/wal replaces the hand-rolled rotation WAL
hhuuggoo Jun 10, 2026
b58eca0
Merge rater fixes: trailing window, one-snapshot anomalies, UTC bucke…
hhuuggoo Jun 10, 2026
4f8a09c
Merge contract fixes: request_id gate, drainer poison isolation, mode…
hhuuggoo Jun 10, 2026
de958a0
gofmt: drain/store_test.go
hhuuggoo Jun 10, 2026
fccb797
Add end-to-end pipeline test: proxy -> emitter -> stream -> drainer -…
hhuuggoo Jun 11, 2026
a764e09
Harden the WAL against failed writes: recover by close-and-reopen
hhuuggoo Jun 11, 2026
1fe70a3
Merge WAL hardening: recover from failed writes (reopen + resync), im…
hhuuggoo Jun 11, 2026
063fbbe
Merge end-to-end pipeline test (proxy → valkey → drainer → Postgres →…
hhuuggoo Jun 11, 2026
f9281c0
Fix B: classify wrapped client-cancel as abort, not upstream fault
hhuuggoo Jun 15, 2026
2758fef
Fix A: emit a zero-token attributable event on pre-header abort
hhuuggoo Jun 15, 2026
ffc132a
Fix C: cap the iolog request body so to_tsvector can't fail the INSERT
hhuuggoo Jun 15, 2026
429db3a
Fix D: correct inverted privacy-rule comment in iolog policy
hhuuggoo Jun 15, 2026
d192cdf
Fix D: ACK decode-poison entries even on the store-outage path
hhuuggoo Jun 15, 2026
89133ac
Fix D: widen rater count casts to bigint (int64) to avoid 32-bit over…
hhuuggoo Jun 15, 2026
5bca057
Fix D: delete dead CountAnomalies surface
hhuuggoo Jun 15, 2026
d397ce1
Fix D: rename overclaiming TestIOLog_NonBlockingSink to what it tests
hhuuggoo Jun 15, 2026
9634d9c
proxy: revert isClientAbort DeadlineExceeded over-reach (regression)
hhuuggoo Jun 15, 2026
a6a2933
Merge pull request #11 from saturncloud/phoebe-full-review-v2
hhuuggoo Jun 15, 2026
8f05353
Merge pull request #12 from saturncloud/phoebe-fast-follow
hhuuggoo Jun 15, 2026
3b22908
Rating: price metered tokens into money from a YAML price book
hhuuggoo Jun 16, 2026
e854bba
Rating: add resource_id to rated_usage grain for customer attribution…
hhuuggoo Jun 16, 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
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.git
.github
bin
*.md
config/settings.example.yaml
66 changes: 66 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: CI
on: push

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.23"
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.64.8

test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.23"
- run: go mod download
- name: Vet
run: go vet ./...
- name: Test
run: make test

integration-test:
name: Integration (live Postgres conformance)
runs-on: ubuntu-latest
services:
postgres:
# Money correctness IS the product: the rating SQL must be exercised
# against a real Postgres (the in-Go oracle pins the formula, but only
# the live DB proves the SQL — and the NUMERIC sum-then-round — match).
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: phoebe_test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 5s
--health-timeout 5s
--health-retries 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.23"
- run: go mod download
- name: Integration test
env:
PHOEBE_TEST_DATABASE_URL: postgres://postgres:postgres@localhost:5432/phoebe_test?sslmode=disable
run: make integration-test
26 changes: 26 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# build artifacts
/bin/
*.exe
*.o
*.a
*.so

# test / coverage
*.out
.coverage

# editor files
**/.idea/
**/.vscode/

# misc
*.DS_Store
*.log

# credentials and key material
*.pem
*.env
config/settings.yaml

# go workspace
vendor/
32 changes: 32 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# golangci-lint configuration for phoebe.
# Run locally with `make lint` (installs the pinned version on demand) or via
# the pre-commit hook. CI runs the same version.
run:
timeout: 5m

linters:
enable:
- errcheck # unchecked errors
- govet # go vet
- ineffassign # ineffectual assignments
- staticcheck # staticcheck analyzers
- unused # unused code
- gofmt # formatting
- goimports # import grouping/ordering
- misspell # common misspellings
- bodyclose # http response bodies closed
- noctx # http requests should carry a context
- unconvert # unnecessary type conversions
- revive # golint successor (style)

linters-settings:
goimports:
local-prefixes: github.com/saturncloud/phoebe

issues:
exclude-rules:
# Test files may leave bodies/errs unchecked for brevity.
- path: _test\.go
linters:
- errcheck
- bodyclose
15 changes: 15 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Pre-commit hooks for phoebe. Install once with `pre-commit install`.
# Mirrors what CI enforces: formatting, vet, and golangci-lint.
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files

- repo: https://github.com/golangci/golangci-lint
rev: v1.64.8
hooks:
- id: golangci-lint
Loading
Loading