go-crypt/docs/development.md
Snider bbf2322389 docs: graduate TODO/FINDINGS into production documentation
Replace internal task tracking (TODO.md, FINDINGS.md) with structured
documentation in docs/. Trim CLAUDE.md to agent instructions only.

Co-Authored-By: Virgil <virgil@lethean.io>
2026-02-20 15:01:55 +00:00

197 lines
5.9 KiB
Markdown

# Development Guide — go-crypt
## Prerequisites
- Go 1.25 or later (the module declares `go 1.25.5`).
- A Go workspace (`go.work`) that resolves the local replace directives for
`forge.lthn.ai/core/go` (at `../go`) and `forge.lthn.ai/core/go-store`
(at `../go-store`). If you are working outside the full monorepo, edit
`go.mod` replace directives to point to your local checkouts.
- No C toolchain, CGo, or system libraries are required.
## Build and Test Commands
```bash
# Run all tests
go test ./...
# Run with race detector (always use before committing)
go test -race ./...
# Run a single test by name
go test -v -run TestName ./...
# Run tests in a specific package
go test ./auth/...
go test ./crypt/...
go test ./trust/...
# Static analysis
go vet ./...
# Run benchmarks
go test -bench=. -benchmem ./crypt/...
go test -bench=. -benchmem ./trust/...
```
There is no build step — this is a library module with no binaries. The
`go vet ./...` check must pass cleanly before any commit.
## Repository Layout
```
go-crypt/
├── auth/ Authentication package
├── crypt/ Cryptographic utilities
│ ├── chachapoly/ Standalone ChaCha20-Poly1305 sub-package
│ ├── lthn/ RFC-0004 quasi-salted hash
│ ├── openpgp/ Service wrapper (core.Crypt interface)
│ ├── pgp/ OpenPGP primitives
│ └── rsa/ RSA OAEP-SHA256
├── docs/ Architecture, development, and history docs
├── trust/ Agent trust model and policy engine
├── go.mod
└── go.sum
```
## Test Patterns
Tests use the `github.com/stretchr/testify` library (`assert` and `require`).
The naming convention follows three suffixes:
| Suffix | Purpose |
|--------|---------|
| `_Good` | Happy path — expected success |
| `_Bad` | Expected failure — invalid input, wrong credentials, not-found errors |
| `_Ugly` | Edge cases — panics, zero values, empty inputs, extreme lengths |
Example:
```go
func TestLogin_Good(t *testing.T) { ... }
func TestLogin_Bad(t *testing.T) { ... }
func TestLogin_Ugly(t *testing.T) { ... }
```
Concurrency tests use `t.Parallel()` and typically spawn 10 goroutines via a
`sync.WaitGroup`. The race detector (`-race`) must pass for all concurrent tests.
## Benchmark Structure
Benchmarks live in `bench_test.go` files alongside the packages they cover.
Benchmark names follow the `BenchmarkFuncName_Context` pattern:
```go
func BenchmarkArgon2Derive(b *testing.B) { ... }
func BenchmarkChaCha20_1KB(b *testing.B) { ... }
func BenchmarkChaCha20_1MB(b *testing.B) { ... }
```
Run benchmarks with:
```bash
go test -bench=. -benchmem -benchtime=3s ./crypt/...
```
Do not optimise without measuring first. The Argon2id KDF is intentionally slow
(~200ms on typical hardware) — this is a security property, not a defect.
## Adding a New Cryptographic Primitive
1. Add the implementation in the appropriate sub-package.
2. Write tests covering `_Good`, `_Bad`, and `_Ugly` cases.
3. Add a benchmark if the function is called on hot paths.
4. Update `docs/architecture.md` with the algorithm reference entry.
5. Run `go vet ./...` and `go test -race ./...` before committing.
## Adding a New Trust Capability
1. Add the `Capability` constant in `trust/trust.go`.
2. Update `isRepoScoped()` in `trust/policy.go` if the capability is
repository-scoped.
3. Update the default policies in `loadDefaults()` in `trust/policy.go`.
4. Add tests covering all three tiers.
5. Update the capability table in `docs/architecture.md`.
## Coding Standards
### Language
UK English throughout: _colour_, _organisation_, _centre_, _artefact_,
_licence_ (noun), _license_ (verb), _behaviour_, _initialise_, _serialise_.
### Go Style
- `declare(strict_types=1)` is a PHP convention; Go has no equivalent. Use
explicit type assertions and avoid `any` except at interface boundaries.
- Every exported function and type must have a doc comment.
- Error strings are lowercase and do not end with a full stop, per Go convention.
- Use the `core.E(op, msg, err)` helper from `forge.lthn.ai/core/go` for
contextual error wrapping: `op` is `"package.Function"`, `msg` is a brief
lowercase description.
- Import groups: stdlib → `forge.lthn.ai/core` → third-party. Separate each
group with a blank line.
### Cryptography
- All randomness from `crypto/rand`. Never use `math/rand` for cryptographic
purposes.
- Use `crypto/subtle.ConstantTimeCompare` for any comparison of secret material
(MACs, hashes). The one exception is `lthn.Verify`, which compares content
identifiers (not secrets) and documents this explicitly.
- Never log or return secrets in error messages. Error strings should be generic:
`"invalid password"`, `"session not found"`, `"failed to decrypt"`.
### Licence
All files are licenced under EUPL-1.2. Do not add files under a different licence.
## Commit Convention
Commits follow the Conventional Commits specification:
```
type(scope): short imperative description
Optional body explaining motivation and context.
Co-Authored-By: Virgil <virgil@lethean.io>
```
Types: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`.
Scopes match package names: `auth`, `crypt`, `trust`, `pgp`, `lthn`, `rsa`,
`openpgp`, `chachapoly`.
Examples:
```
feat(auth): add SQLite session store for crash recovery
fix(trust): reject empty ScopedRepos as no-access for Tier 2
test(crypt): add benchmark suite for Argon2 and ChaCha20
```
## Forge Push
The canonical remote is `forge.lthn.ai`. Push via SSH only; HTTPS authentication
is not configured:
```bash
git push forge main
# remote: ssh://git@forge.lthn.ai:2223/core/go-crypt.git
```
## Local Replace Directives
The `go.mod` contains:
```
replace (
forge.lthn.ai/core/go => ../go
forge.lthn.ai/core/go-store => ../go-store
)
```
Do not modify these paths. If you need to work with a different local checkout,
use a Go workspace (`go.work`) at the parent directory level rather than editing
the replace directives directly.