Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
fd295fc
chore: ignore superpowers artifacts and local CLAUDE.md
ceballosiker May 12, 2026
21f8692
chore: initialize go module and makefile
ceballosiker May 12, 2026
4c9db18
feat(gcp): add ExitNode, ProvisionOpts, State enum with parser
ceballosiker May 12, 2026
5ba6d79
feat: declare Provider, TailscaleClient, PFSenseClient, Probe interfaces
ceballosiker May 12, 2026
c5f6568
feat(config): add TOML loader + env-var secret resolution
ceballosiker May 12, 2026
ff8ad5b
feat(state): add flock-protected JSON cache for active exit node
ceballosiker May 12, 2026
052dd98
test(state): add flock contention test
ceballosiker May 12, 2026
103b2b1
feat(core): add Core struct, Deps, New ctor, CriticalError
ceballosiker May 12, 2026
8c9b8d0
test(core): add hand-written mocks for Provider, TS, PF, Probe
ceballosiker May 12, 2026
56318b7
feat(core): rotate happy path with mocked deps (no pfsense branch)
ceballosiker May 12, 2026
d21a15f
feat(core): rotate pfSense cutover branch with revert discipline
ceballosiker May 12, 2026
20e9804
test(core): cover all pre-pfsense rotate failure cases
ceballosiker May 12, 2026
38bb0e5
test(core): cover pfsense update/apply failure with successful revert
ceballosiker May 12, 2026
b3053f8
test(core): CRITICAL case — apply + revert both fail leaves both node…
ceballosiker May 12, 2026
0b3f389
test(core): post-cutover probe failure reverts and destroys new
ceballosiker May 12, 2026
3469766
test(core): old-node teardown failures do not abort rotate
ceballosiker May 12, 2026
026a4ea
feat(core): add idempotent Up command
ceballosiker May 12, 2026
5e272af
feat(core): add Down command (stop / destroy)
ceballosiker May 12, 2026
1fab9f2
feat(core): add List and Status with drift detection
ceballosiker May 12, 2026
4e7ff67
feat(core): add Health probe-only command
ceballosiker May 12, 2026
bf6b0ce
feat(core): add idempotent SyncPFSense
ceballosiker May 12, 2026
9003b75
feat(core): add EstimateCost with static price table
ceballosiker May 12, 2026
373666c
chore: add golangci-lint config
ceballosiker May 12, 2026
955a528
Merge pull request #1 from ceballosiker/feat/plan-1-foundation-core
ceballosiker May 12, 2026
a97fc01
chore: add tailscale v2, gcp compute apiv1 dependencies
ceballosiker May 12, 2026
5d866fa
feat(verify): add commandRunner exec seam with fake runner
ceballosiker May 12, 2026
b4212cd
feat(verify): EgressVia happy path with deferred exit-node restore
ceballosiker May 12, 2026
9dbbd70
test(verify): restore runs on every EgressVia failure path
ceballosiker May 12, 2026
88354dd
feat(verify): EgressDirect for post-cutover probe
ceballosiker May 12, 2026
63ccfb3
test(verify): interface conformance sentinel for shellProbe
ceballosiker May 12, 2026
4ec99f5
feat(pfsense): add REST client constructor + envelope decoder
ceballosiker May 12, 2026
5928052
feat(pfsense): GetGateway via /api/v2/routing/gateway
ceballosiker May 12, 2026
e8c4da6
feat(pfsense): UpdateGatewayIP via PATCH
ceballosiker May 12, 2026
3864b89
feat(pfsense): Apply endpoint + interface sentinel
ceballosiker May 12, 2026
6a80200
feat(tailscale): v2client constructor with OAuth client-credentials
ceballosiker May 12, 2026
672b96d
feat(tailscale): MintEphemeralAuthKey via Keys().CreateAuthKey
ceballosiker May 12, 2026
06262fe
feat(tailscale): WaitForDevice with polling
ceballosiker May 12, 2026
92ce887
feat(tailscale): AuthorizeExitNode = SetAuthorized + SetSubnetRoutes
ceballosiker May 12, 2026
341de16
feat(tailscale): SetTags + DeleteDevice
ceballosiker May 12, 2026
4244551
feat(gcp): constructor with SA-JSON or ADC fallback
ceballosiker May 12, 2026
0b23c7d
feat(gcp): buildInstanceResource with shape unit test
ceballosiker May 18, 2026
53064be
feat(gcp): Provision wires Insert → Wait → Get
ceballosiker May 18, 2026
2af7200
fix(gcp): instanceToExitNode breaks on first NatIP + helper tests
ceballosiker May 18, 2026
d36e9bb
feat(gcp): Start, Stop, Destroy with AggregatedList zone resolution
ceballosiker May 18, 2026
6ea4b9c
refactor(gcp): sentinel ErrInstanceNotFound + hoist managed-by constants
ceballosiker May 18, 2026
9bef0b3
feat(gcp): List (AggregatedList) + Get (by name)
ceballosiker May 19, 2026
4c86b88
refactor(gcp): findInstance returns proto to drop double-fetch in Get
ceballosiker May 19, 2026
7ab0b61
feat(gcp): PickZoneInRegion helper (not on Provider iface)
ceballosiker May 19, 2026
7662c08
polish(gcp): PickZoneInRegion server-side UP filter + docs + region g…
ceballosiker May 19, 2026
4b7a97c
test(gcp): add integration test scaffold (build-tagged)
ceballosiker May 20, 2026
22b737b
polish(gcp): integration test surfaces Close error + drops dead bool
ceballosiker May 20, 2026
9260ccb
feat(install): VM bootstrap script reading metadata-provided creds
ceballosiker May 20, 2026
7db7eec
docs: professional README with architecture, dev workflow, and roadmap
ceballosiker May 20, 2026
b6e1db2
Merge pull request #2 from ceballosiker/feat/plan-2-clients
ceballosiker May 20, 2026
eec3e8d
feat(core): Start(name) with active-state refresh
ceballosiker May 20, 2026
ef3e21e
polish(core): wrap provider.Start error with node name
ceballosiker May 20, 2026
b5581bd
feat(core): Stop(name) with active-state refresh
ceballosiker May 20, 2026
c1ab03b
feat(core): Destroy(name) with TS device cleanup and state clear
ceballosiker May 20, 2026
2a4b87e
docs(examples): annotated reference config.toml + parse test
ceballosiker May 20, 2026
476facd
feat(cli): root command + global flags + buildCore helper
ceballosiker May 20, 2026
8119a70
feat(cli): exitnode up (idempotent provision)
ceballosiker May 20, 2026
e714ce9
feat(cli): exitnode down [--destroy]
ceballosiker May 20, 2026
882cd1b
feat(cli): exitnode rotate [--region]
ceballosiker May 20, 2026
8d401ff
feat(cli): exitnode list [--json]
ceballosiker May 20, 2026
a4405bc
feat(cli): exitnode status [--json]
ceballosiker May 20, 2026
2137acd
feat(cli): exitnode health (exits 2 on probe fail)
ceballosiker May 20, 2026
62d25e8
feat(cli): exitnode pfsense sync
ceballosiker May 20, 2026
d79f767
feat(cli): exitnode cost [--period]
ceballosiker May 20, 2026
491d5fc
feat(mcp): server scaffolding + buildCore helper
ceballosiker May 20, 2026
06aec1c
feat(mcp): wire all 10 tools to core methods
ceballosiker May 20, 2026
767c4be
docs(examples): mcp.json snippet for Claude Desktop / OpenClaw
ceballosiker May 20, 2026
a758b3c
ci: add github actions workflow (vet, lint, test, cross-build matrix)
ceballosiker May 20, 2026
a05eaa8
ci(release): goreleaser config + release workflow + --version flag
ceballosiker May 20, 2026
0068436
polish(mcp): wire version var into Implementation.Version
ceballosiker May 20, 2026
bdd7aa2
docs(readme): prereqs/install/configure/mcp/troubleshooting sections
ceballosiker May 20, 2026
0db191e
chore: go mod tidy (Plan 3 closes out staged indirects)
ceballosiker May 20, 2026
9244a9f
docs(readme): drop 3-plan roadmap; describe v0.1 surface + deferred i…
ceballosiker May 20, 2026
f7bd29f
docs(readme): add personal-project disclaimer
ceballosiker May 20, 2026
1e536f4
ci(lint): pin golangci-lint to v2.x (v1.x is built with go 1.24 < 1.25)
ceballosiker May 20, 2026
4b519c4
ci(lint): upgrade to golangci-lint v2.12 (action v9, new v2 config)
ceballosiker May 20, 2026
d83bf59
fix(lint): address v2 staticcheck/errcheck/gofmt findings
ceballosiker May 20, 2026
17764e6
Merge pull request #3 from ceballosiker/feat/plan-3-binaries
ceballosiker May 20, 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
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:
branches: [main, develop]
pull_request:

permissions:
contents: read

jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.25'
check-latest: true
cache: true
- run: go vet ./...
- run: go test -race -short ./...

lint:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.25'
cache: true
- uses: golangci/golangci-lint-action@v9
with:
# golangci-lint v1.x was built with Go 1.24 and refuses to analyze
# source targeting go 1.25 (which our go.mod pins). Pin a recent
# v2.x; the action's @v6/@v7/@v8 don't speak v2 config format.
version: v2.12.2
args: --timeout=5m

build-matrix:
name: build ${{ matrix.goos }}/${{ matrix.goarch }}
runs-on: ubuntu-latest
strategy:
matrix:
goos: [linux, darwin]
goarch: [amd64, arm64]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.25'
cache: true
- name: build exitnode
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: '0'
run: go build -trimpath -o /dev/null ./cmd/exitnode
- name: build exitnode-mcp
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: '0'
run: go build -trimpath -o /dev/null ./cmd/exitnode-mcp
26 changes: 26 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: release

on:
push:
tags: ['v*']

permissions:
contents: write

jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version: '1.25'
cache: true
- uses: goreleaser/goreleaser-action@v6
with:
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,9 @@ go.work.sum
# Editor/IDE
# .idea/
# .vscode/

# Superpowers working artifacts (specs, plans, review notes) — never committed
docs/superpowers/

# Local Claude project guidance — never committed
CLAUDE.md
29 changes: 29 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
version: "2"

# Standard preset = errcheck, govet, ineffassign, staticcheck, unused.
# Matches what golangci-lint v1 enabled implicitly. Add more linters
# here only after the standard set is consistently green.
linters:
default: standard
settings:
errcheck:
# Stdout-write errors are not meaningfully recoverable in this CLI;
# do not require every fmt.Fprint* / Fprintln / Fprintf call to be
# blank-assigned.
exclude-functions:
- fmt.Fprint
- fmt.Fprintf
- fmt.Fprintln
exclusions:
rules:
# Test files routinely defer Close() on stores/servers without checking
# the error — the goroutine is already torn down and the next test will
# surface any real problem.
- path: _test\.go
linters:
- errcheck

formatters:
enable:
- gofmt
- goimports
64 changes: 64 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
version: 2

project_name: exitnode

before:
hooks:
- go mod download

builds:
- id: exitnode
main: ./cmd/exitnode
binary: exitnode
env:
- CGO_ENABLED=0
goos: [linux, darwin]
goarch: [amd64, arm64]
flags: [-trimpath]
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
- -X main.date={{.Date}}
- id: exitnode-mcp
main: ./cmd/exitnode-mcp
binary: exitnode-mcp
env:
- CGO_ENABLED=0
goos: [linux, darwin]
goarch: [amd64, arm64]
flags: [-trimpath]
ldflags:
- -s -w
- -X main.version={{.Version}}

archives:
- id: default
formats: [tar.gz]
name_template: >-
{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}
files:
- LICENSE
- README.md
- examples/config.toml
- examples/mcp.json
- scripts/install.sh

checksum:
name_template: 'checksums.txt'
algorithm: sha256

changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- '^chore:'

release:
github:
owner: ceballosiker
name: exit-node
draft: false
prerelease: auto
26 changes: 26 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.PHONY: build test test-integration lint vet install clean

GO ?= go

build:
$(GO) build -o bin/exitnode ./cmd/exitnode
$(GO) build -o bin/exitnode-mcp ./cmd/exitnode-mcp

test:
$(GO) test -race -short ./...

test-integration:
EXITNODE_INTEGRATION=1 $(GO) test -race -tags integration ./...

vet:
$(GO) vet ./...

lint:
golangci-lint run

install:
$(GO) install ./cmd/exitnode
$(GO) install ./cmd/exitnode-mcp

clean:
rm -rf bin/
Loading
Loading