This file explains repo-wide conventions for the Raybeam SSH key store and where to find scoped rules.
Precedence: the closest AGENTS.md to your changes wins. Root holds global defaults only.
Raybeam is a lightweight SSH public key store written in Go with LDAP authentication. It provides a REST API for managing SSH keys with role-based access control.
- Language: Go 1.24+ (toolchain 1.25.1)
- Frameworks: Fiber v2 (HTTP), BoltDB (storage), simple-ldap-go (auth)
- Entry points:
main.go→cmd/(Cobra CLI) →internal/server/(HTTP handlers) - Documentation: See
docs/for comprehensive guides
- Keep diffs small: PRs should be <300 net LOC (excluding locks/generated files); split larger changes
- Atomic commits: Use Conventional Commits format (
feat(scope):,fix:,docs:) - Ask first before:
- Adding heavy dependencies or changing core frameworks
- Running full end-to-end test suites in CI
- Repo-wide refactors that touch >10 files
All commands must pass before committing:
# Format code
go fmt ./...
# Vet code
go vet ./...
# Run tests
go test ./... -race -cover
# Build to ensure no compilation errors
go build -o raybeam .- Format:
gofmt -s(tabs for indentation, enforced by.editorconfig) - Naming: Exported functions/types use
PascalCase, private usecamelCase - Errors: Return errors explicitly; use
errors.New()for sentinels; wrap withfmt.Errorf("%w", err) - Comments: Document all exported types and functions with godoc comments
- Imports: Group stdlib → external → internal, use
goimportsfor sorting
- No secrets in VCS: LDAP passwords via environment variables or secret managers only
- Input validation: SSH keys validated via
golang.org/x/crypto/ssh.ParseAuthorizedKey - Database permissions: BoltDB file must have
0600permissions (owner read/write only) - LDAP over TLS: Use
ldaps://in production; never plainldap:// - Dependencies: All deps must be latest stable; run
go mod tidyand verify checksums
- Coverage goal: 70%+ for handlers, 80%+ for models
- Table-driven tests: Prefer
[]struct{name, input, want, wantErr}pattern - Mocking: Use interfaces for LDAP/DB mocking; avoid real LDAP in unit tests
- Test tiers:
- Unit (default, no tags):
go test ./... - Integration (
//go:build integration): drivesinternal/ldap_service/ auth paths against a real OpenLDAP testcontainer. Run withgo test -tags integration -race ./.... Requires Docker. - E2E (
//go:build e2e): boots the full raybeam HTTP server on an ephemeral port against a real OpenLDAP testcontainer and hits it with anhttp.Client. Run withgo test -tags e2e -race ./.... Requires Docker. - Test scaffolding lives in
internal/testsupport/ldapcontainer(shared) andinternal/testsupport/raybeamserver(e2e only). All testsupport files are gated by build tags and do not land in the defaultgo build/go testoutput.
- Unit (default, no tags):
Example test pattern:
func TestGetKeysForUser(t *testing.T) {
tests := []struct {
name string
dn string
want []SSHKey
wantErr bool
}{
{"empty", "CN=User", []SSHKey{}, false},
// ... more cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// test implementation
})
}
}- Code formatted with
go fmt - All tests pass (
go test ./...) - No new
go vetwarnings - Godoc comments added for exported items
- Updated
docs/if API or behavior changed - Conventional commit message format
- No secrets or sensitive data in commit
Good examples to follow:
internal/models/ssh_key.go— Clean CRUD operations with godoc commentsinternal/server/auth_middleware.go— Comprehensive error handlinginternal/server/auth_middleware_test.go— Table-driven tests
Patterns to avoid:
- Naked returns in functions >10 lines
panic()for non-initialization errors- Ignoring errors with
_ = - HTTP handlers without proper status codes
- Check
docs/directory:docs/architecture.md— system design and data flowdocs/api.md— endpoint specificationsdocs/development.md— setup and workflow
- Read godoc comments in relevant packages
- Open a draft PR with
[WIP]prefix to discuss approach - Ask in GitHub Discussions for design questions
- Format: Conventional Commits (
feat(server):,fix(models):,docs:) - Breaking changes: Add
BREAKING CHANGE:footer; requires major version bump - Branch naming:
feature/description,fix/issue-123,docs/update-api
- Go principles: Follow standard Go idioms from Effective Go
- SOLID principles: Single responsibility, composition over inheritance
- Error handling: Errors are values; handle explicitly; don't use exceptions
- Interfaces: Keep small and focused; accept interfaces, return structs
- Stable versions only: No pre-releases unless critical security fix
- Minimal deps: Avoid adding deps for trivial functionality
- Security: Run
go list -m all | nancy sleuthfor vulnerability checks - Updates: Renovate bot handles automated updates; review carefully
- Primary sources: Go official docs, library godoc, vendor documentation
- Uncertainty: Check implementation in vendor code or add TODO for clarification
- API contracts: Verify behavior with tests rather than assumptions
- REST principles: Follow HTTP semantics (GET idempotent, POST creates, etc.)
- Status codes: Use appropriate codes (200, 201, 401, 403, 404, 500)
- Versioning: Not currently versioned; breaking changes require v2 module path
- Content negotiation: Support
text/plainandapplication/jsonvia Accept header
- Secret management: Environment variables or mount secrets; never hardcode
- Input validation: Validate all user input; sanitize for logs
- LDAP auth: Verify credentials on every request (stateless design)
- Dependency scanning: CI runs security checks on all deps
- Logging: Fiber middleware logs all HTTP requests (method, path, status, duration)
- Structured logs: Use
log.Printfwith consistent format; consider structured logger for v2 - Metrics: Not yet implemented;
/infoendpoint provides version info - Health checks: Use GET
/infofor liveness checks
- Project license: MIT (see LICENSE file)
- Dependencies: Verify all deps have compatible licenses (MIT, BSD, Apache 2.0)
- SPDX: Not currently used; acceptable for future additions
Currently, Raybeam has a simple structure with no nested AGENTS.md files:
./cmd/AGENTS.md— Not yet created (CLI commands and flag parsing)./internal/AGENTS.md— Not yet created (See sections below for inline guidance)
Inline scope for internal packages:
-
internal/models/ — Database models and CRUD operations
- Pattern: Keep DB logic in models, not handlers
- Style: Godoc all exported functions; return errors explicitly
-
internal/server/ — HTTP server, routes, and middleware
- Pattern: Middleware → handler → model → response
- Style: Use Fiber context methods; return errors via
sendError()
-
internal/build/ — Build metadata (version from VCS)
- Pattern: Read-only runtime reflection
- Style: Keep minimal; no business logic
- Nearest AGENTS.md wins: Scoped rules override root
- Explicit user prompts override files: User intent is authoritative
- EditorConfig wins for formatting:
.editorconfigdefines whitespace rules - Go conventions trump personal style: Follow Effective Go and Go Code Review Comments
- No golangci-lint config: Using standard
go vetandgo fmtfor simplicity; add.golangci.ymlif needed - No Makefile yet: Added below as fundamental; wraps common commands
- EditorConfig present: Respects tabs for Go, spaces for YAML/Markdown
- Tests use env vars for LDAP: Integration tests skip if LDAP not configured; unit tests mock where possible