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
16 changes: 8 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ jobs:
vet:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: "1.25"
go-version-file: "go.mod"
cache: false
- run: go vet ./...

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: "1.25"
go-version-file: "go.mod"
cache: false
- run: CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /tmp/httphq ./src

Expand All @@ -34,7 +34,7 @@ jobs:
contents: read
packages: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- id: meta
run: |
Expand All @@ -55,7 +55,7 @@ jobs:

- uses: docker/setup-buildx-action@v4

- uses: docker/build-push-action@v6
- uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ jobs:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: "1.25"
go-version-file: "go.mod"
cache: false
- run: go test ./...
e2e:
Expand All @@ -25,14 +25,14 @@ jobs:
run:
working-directory: e2e
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: "1.25"
go-version-file: "go.mod"
cache: false
- uses: actions/setup-node@v6
- uses: actions/setup-node@v7
with:
node-version: "22"
node-version-file: ".tool-versions"
- name: Build server
run: CGO_ENABLED=0 go build -o ./bin/httphq ./src
working-directory: .
Expand Down
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodejs 22.23.1
53 changes: 53 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# AGENTS.md

Guidance for agents and contributors working in this repository.

## Comments

Comments describe what the code does now and warn about non-obvious constraints
or regressions. They are not a changelog, bug tracker, or ticket index. Write
them so they still make sense in a year.

- No ticket or PR references.
- No bug war stories: don't narrate a specific bug and how it was fixed ("this
fixes the flicker when..."). Once fixed, that history is noise.
- No hyper-specific framing: don't tie comments to one-time scenarios; describe
the generic, reusable purpose instead.
- Explain intent and non-obvious behavior: why this branch exists, what
invariant it protects.
- Flag regression risks the next dev must respect (e.g. "excluded by default so
new surfaces are safe").
- Keep comments generic and reusable, especially in shared helpers and test
fixtures.

## Client IP is a trust decision

`PLATFORM` selects which header the real client IP is read from, and setting it
trusts that header unconditionally — httphq cannot tell a platform's header
from one a client forged. Inbound traffic must not be able to reach the process
bypassing that platform, or a client can spoof its IP and evade rate limiting.
Leaving it unset behind a proxy is the opposite failure: every request looks
like it came from the proxy and rate limiting becomes global.

## Captured data is ephemeral

SQLite writes to the container's writable layer. Capture history is lost on
restart, by design — nothing here is a durable store, and no migration path
exists for it.

## Logging

Structured JSON to stdout via `log/slog`, with OpenTelemetry field names
(`service.name`, `http.request.method`, `url.path`, ...). Every request carries
a `request_id` that is reused from a valid inbound `X-Request-Id` or minted,
echoed on the response, and stamped onto every line emitted while handling it.

Headers and bodies are never logged and paths are logged without their query
string; a denylist masks sensitive keys as a backstop. Probe traffic to
`/api/health` logs at debug so it stays out of production logs.

## The listen port is a constant

`port` in `src/application.go` is not configurable. Anything that needs to run
two instances, or to run alongside something already holding 8080, has to
change the constant.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
See [AGENTS.md](AGENTS.md).
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@ without the noise of whatever provider sits in front of httphq.
> behind a proxy but leave `PLATFORM` unset, every request appears to come
> from the proxy and rate limiting becomes global.

## Logging

httphq logs to stdout as structured JSON via the standard library's `log/slog` — one JSON object per line, with no log files or shipping built in, so any collector can pick the logs up. Field names follow OpenTelemetry conventions (`service.name`, `http.request.method`, `url.path`, `http.response.status_code`, ...). Every request gets a correlation `request_id` (a valid inbound `X-Request-Id` is reused, otherwise one is minted) that is echoed back on the response header and stamped onto every log line emitted while handling that request. Each request produces one access-log line; headers and bodies are never logged, paths are logged without their query string, and a denylist masks sensitive keys as a backstop. The level defaults to `info` in production (`debug` elsewhere) and is overridable with `LOG_LEVEL`; Kubernetes probe traffic to `/api/health` logs at `debug` so it stays out of production logs.

## License

[MIT](https://opensource.org/licenses/MIT)
26 changes: 22 additions & 4 deletions e2e/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
},
"devDependencies": {
"@playwright/test": "^1.50.0",
"typescript": "^5.6.0"
"@types/node": "^22.20.1",
"typescript": "^6.0.3"
}
}
13 changes: 13 additions & 0 deletions e2e/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"lib": ["ES2022", "DOM"],
"module": "preserve",
"moduleResolution": "bundler",
"noEmit": true,
"skipLibCheck": true,
"strict": true,
"target": "ES2022",
"types": ["node"]
},
"include": ["tests/**/*.ts", "playwright.config.ts"]
}
Loading