Imported from canonical/authd (
AGENTS.md). Install upstream withnpx skills add canonical/authd. Copyright stays with the author.
authd AI Coding Instructions
Project Overview
authd is an authentication daemon for cloud-based identity providers (MS Entra ID, Google IAM). It's a hybrid Go/Rust/C project that provides:
- authd daemon (Go): Main authentication service with gRPC API
- PAM modules (Go): Two implementations - native shared library for GDM, and C-wrapper+executable for other PAM apps
- NSS module (Rust): Name Service Switch integration for user/group lookups
- Brokers (Go): Pluggable D-Bus-based providers that interface with identity providers
Architecture Fundamentals
Component Communication
- Internal: gRPC for PAM/NSS ↔ authd (defined in
internal/proto/authd/authd.proto) - External: D-Bus for authd ↔ brokers (interface in
examplebroker/com.ubuntu.auth.ExampleBroker.xml) - Daemon: Systemd socket activation via
internal/daemon/daemon.go - Data flow: PAM/NSS → gRPC → authd → D-Bus → broker → identity provider
Key Directories
cmd/authd/,cmd/authctl/: Main binariesinternal/brokers/: Broker manager and D-Bus integrationinternal/services/: gRPC service implementations (PAM, NSS, user management)internal/users/: User/group database management (SQLite + BoltDB legacy)pam/: PAM module with two build modes (seepam/README.md)nss/: Rust NSS module usinglibnsscrateexamplebroker/: Reference broker implementation
Building & Testing
Build Commands
# Full Debian package (includes all components + tests)
debuild --prepend-path=${HOME}/.cargo/bin
# Individual components (development)
go build ./cmd/authd # authd daemon only
go generate ./pam/ && go build -o ./pam/authd-pam ./pam # PAM helper client
cargo build # NSS (debug mode)
Testing Conventions
- Run tests:
go test ./...(add-racefor race detection) - Golden files: Use
internal/testutils/goldenpackage- Update with
TESTS_UPDATE_GOLDEN=1 go test ./... - Compare/update:
golden.CheckOrUpdate(t, got)orgolden.CheckOrUpdateYAML(t, got)
- Update with
- Test helpers with underscores: Functions prefixed
Z_ForTests_are test-only exports (e.g.,Z_ForTests_CreateDBFromYAML) - Environment variables:
AUTHD_SKIP_ROOT_TESTS=1: Skip tests that fail when run as root
- Broker provider build tags: An untagged broker test run does not compile
the provider-specific wiring. When changing broker code, run tests for every
provider configuration:
Thego -C authd-oidc-brokers test ./... go -C authd-oidc-brokers test -tags withgoogle ./... go -C authd-oidc-brokers generate --tags withmsentraid ./internal/providers/msentraid/... go -C authd-oidc-brokers test -tags withmsentraid ./...withmsentraidgeneration step requires the recursivelibhimmelblausubmodule and generates thehimmelblau.hand library artifacts. - Broker linting: Pass provider tags explicitly, for example:
scripts/golangci-lint -C authd-oidc-brokers run --build-tags withmsentraid. Use--build-tags withgooglefor Google-specific changes.
Code Generation
Critical: Run go generate before building PAM or when modifying protobuf files:
go generate ./pam/ # PAM module (creates .so files)
go generate ./internal/proto/authd/ # Regenerate protobuf
go generate ./shell-completion/ # Shell completions
Project-Specific Patterns
Broker Integration
- Brokers are discovered from
/usr/share/authd/brokers/*.conf(D-Bus service files) - First broker is always the local broker (no config file)
- Manager in
internal/brokers/manager.gohandles session→broker and user→broker mappings - Brokers must implement the D-Bus interface defined in
internal/brokers/dbusbroker.go
PAM Module Dual Mode
The PAM module has two implementations (see pam/README.md):
- GDM mode (
pam_authd.so): Native Go shared library with GDM JSON protocol support - Generic mode (
pam_authd_exec.so+authd-pamexecutable): C wrapper launching Go program via private D-Bus- Required for reliability with non-GDM PAM apps (avoids Go threading issues)
Database & User Management
- Migrating from BoltDB to SQLite:
internal/users/db/handles both - User/group data cached locally in
/var/lib/authd/authd.db - ID allocation:
internal/users/idlimitsgenerator/generates UID/GID ranges - Group file updates:
internal/users/localentries/handles local system files
Testing Patterns
- Use
testify/requirefor assertions (notassert) - Golden files in
testdata/golden/subdirectories matching test structure - Test-only exports via
export_test.gofiles (no build tag, package-level visibility) - PAM integration tests use
ptytestinpam/integration-tests/
Common Workflows
Adding a gRPC Service Method
- Update
internal/proto/authd/authd.proto - Run
go generate ./internal/proto/authd/ - Implement in service (e.g.,
internal/services/pam/pam.go) - Add tests with golden files
Creating a New Broker
- Implement D-Bus interface from
examplebroker/com.ubuntu.auth.ExampleBroker.xml - Create
.conffile in/usr/share/authd/brokers/ - Register D-Bus service with systemd
Debugging
- Logs via
github.com/canonical/authd/logpackage (supports systemd journal) - Enable debug:
authd daemon -vvv(3 levels of verbosity) - Socket path:
/run/authd.sock(override withAUTHD_NSS_SOCKETfor NSS tests)
Git Usage
Always pass --no-pager to git commands that may invoke a pager, to prevent them from hanging:
git --no-pager diff
git --no-pager show
git --no-pager log
Agent-authored changes should generally be committed once complete. Do not leave finished agent-created changes uncommitted unless the user explicitly asks for no commit.
When creating commits for agent-authored changes, use atomic commits: each commit must contain one coherent, self-contained logical change.
Commit messages
Explain why, not what — the diff shows what changed.
- For bug fixes, describe the observable symptom before the root cause
- Document non-obvious decisions and rejected alternatives
- One-liners are fine for mechanical changes; anything behavioral needs a body
- Try to keep the subject line at 72 characters or less; wrap body lines at 72 characters (URLs that cannot be split are the only accepted exception)
Don't narrate your activity ("Fixed X as requested") or describe the diff ("Add null check before calling Process()").
How to commit with correct wrapping
Use git commit -F - with a heredoc and insert explicit newlines at
word boundaries before reaching 72 characters. Do not use
git commit -m "..." for multi-line messages — it will not wrap.
Dependencies & Tools
- Go: See
go.modfor version requirements, uses go modules with vendoring - Rust: Cargo with vendor filtering (see
Cargo.tomlworkspace) - Required:
libpam-dev,libglib2.0-dev,protoc,cargo-vendor-filterer - Optional:
delta(colored diffs in tests)
Code Style
- Follow Effective Go for Go style conventions
- Use
go fmtandgofmt -s - Rust: Standard cargo fmt conventions
Prose Style
Write all prose in plain, simple English. This includes code comments, commit messages, and documentation. Use short sentences and everyday words. Do not omit technical details.
Linting
After making changes to Go files, run scripts/golangci-lint to check for lint errors:
scripts/golangci-lint run
If the changed files are below authd-oidc-brokers/, use the -C flag to run the linter in that directory:
scripts/golangci-lint -C authd-oidc-brokers run