Thanks for your interest in contributing!
# Clone and build
git clone https://github.com/nnemirovsky/sluice.git
cd sluice
make build
# Run tests
make testmake fmt # gofumpt formatting
make lint # golangci-lint
make test # all unit testsmake test-- runs all unit tests with-v -count=1make test-coverage-- generates HTML coverage report incoverage.html- CI enforces a minimum 75% overall coverage threshold
E2e tests live in e2e/ and require the e2e build tag. They start a real sluice binary and test the full proxy, credential injection, MCP gateway, and audit log flows.
make test-e2e # run all e2e tests locally
make test-e2e-docker # run Linux e2e tests via Docker Compose
make test-e2e-macos # run macOS e2e tests (Apple Container)Build tags:
e2e-- required for all e2e testse2e && linux-- Docker compose integration tests (requires Docker)e2e && darwin-- Apple Container tests (requires macOS withcontainerCLI)
When writing new e2e tests, use the helpers in e2e/helpers_test.go (startSluice, connectSOCKS5, startEchoServer, etc.) to avoid boilerplate.
- Apple Container and macOS VM (tart) integration tests (
internal/container/) use mockCommandRunnerby default and run on all platforms - Full integration tests requiring a real Apple Container runtime are in
e2e/apple_test.go(seedocs/apple-container-quickstart.md) - macOS VM tests require
tartCLI (brew install cirruslabs/cli/tart) and Apple Silicon. Unit tests use mockedCommandRunnerand run everywhere. E2e tests with a real macOS VM requiretartinstalled and a compatible OCI image.
Use scoped Conventional Commits:
feat(audit): blake3 hash chain for tamper-evident audit log
fix(proxy): make SIGHUP policy reload race-free
refactor(vault): consolidate credential name validation
test(mcp): add comprehensive gateway unit tests
Prefix branches with the change type:
feat/hashicorp-vault-provider
fix/sighup-reload-race
refactor/approval-broker
The REST API uses spec-first code generation. The OpenAPI spec is the source of truth.
# 1. Edit the OpenAPI spec
# api/openapi.yaml
# 2. Regenerate Go types, server interface, and chi router
make generate
# 3. Implement or update handler methods in internal/api/server.go
# The generated ServerInterface defines the method signatures
# 4. Test
go test ./internal/api/ -v
# 5. Lint the spec (optional, requires Node.js)
make lint-apiDo not edit internal/api/api.gen.go manually. It is regenerated from the spec.
internal/store/-- SQLite-backed policy store for all runtime stateinternal/proxy/-- SOCKS5 server, HTTPS MITM, SSH jump host, IMAP/SMTP proxyinternal/policy/-- Policy engine with glob pattern matching (compiled from SQLite store)internal/vault/-- Credential storage and pluggable provider backends (age, env, HashiCorp Vault, 1Password, Bitwarden, KeePass, Gopass)internal/mcp/-- MCP gateway with tool policy enforcementinternal/api/-- REST API handlers (spec-first, generated fromapi/openapi.yamlvia oapi-codegen)internal/channel/-- Channel interface, ChannelType enum, and approval Broker (channel-agnostic)internal/channel/http/-- HTTP webhook channel (HMAC-signed delivery, sync/async approval)internal/telegram/-- TelegramChannel implementation of channel.Channel interfaceinternal/audit/-- Append-only JSON lines logger with blake3 hash chaininginternal/container/-- ContainerManager interface, Apple Container backend, macOS VM (tart) backend (CLI wrappers, pf routing, CA cert injection)internal/docker/-- Docker container backend implementing ContainerManager with hot credential reloadcmd/sluice/-- CLI entrypoint and subcommands (policy, mcp, cred, cert, audit, channel) with--runtimeflag for backend selection
See CLAUDE.md for detailed architecture documentation.
To add a new credential provider backend:
- Create
internal/vault/provider_<name>.goimplementing theProviderinterface (Get,List,Name). - Add a config struct (e.g.
FooConfig) toVaultConfigininternal/vault/provider.go. - Add a
case "<name>"tonewSingleProviderininternal/vault/provider.go. - Add migration columns for provider-specific config to
internal/store/migrations/. - Update
GetConfig/UpdateConfigininternal/store/store.goto read/write the new columns. - Update TOML import in
internal/store/import.goto parse the new[vault.<name>]section. - Write tests in
internal/vault/provider_<name>_test.gousing mocks (no live service calls). - Add commented examples to
examples/config.toml.
All providers must return SecureBytes from Get() and should prefer pure Go (no CGO) for Docker compatibility.
By contributing, you agree that your contributions will be licensed under the MIT License.