Architecture
Clipboard Manager is built with clean architecture principles: a clear separation between the API surface, the business logic, and the infrastructure, with components communicating through interfaces so each layer can be tested on its own.
A learning project too
Beyond being a useful tool, this codebase is a deliberate exercise in Go
project structure and software design patterns.
Layout
- cmd
- clipd
- clipctl
- internal
- api
- service
- storage
- daemon
- monitor
- analyzer
- client
- cli
- config
- logger
- domain
- Makefile
What each package owns:
| Package | Responsibility |
|---|---|
api | HTTP handlers and routes (chi) |
service | Business logic |
storage | SQLite and in-memory stores |
daemon | Orchestration, PID file, signals, retention |
monitor | Clipboard polling |
analyzer | Sensitive-content detection |
client | HTTP client used by clipctl |
cli | Command dispatch for clipctl |
config | Flags and defaults |
logger | Structured logging with rotation |
domain | The core entry model |
How a clipboard change flows
- monitor polls the system clipboard on the configured interval and reports changes.
- analyzer inspects the new content. If it matches a sensitive pattern (passwords, bearer tokens, API keys), the entry is dropped with a reason and never stored.
- service applies the business rules and hands the entry to storage.
- storage persists it to SQLite.
- api exposes the history over the Unix socket; clipctl consumes that same API through the client package.
Design choices
- Interfaces at the boundaries. The service layer depends on a storage interface, not on SQLite. An in-memory store implements the same interface, which is what the table-driven service tests run against.
- Unix socket, not TCP. The API is unreachable from the network by construction, and filesystem permissions (0600) are the access control.
- The daemon owns lifecycle concerns. PID file management, signal handling, graceful shutdown with a deadline, and the retention loop all live in the daemon package, away from the business logic.
Sensitive-content detection
The analyzer keeps history safe with three regex families, matched case-insensitively:
| Pattern | Matches |
|---|---|
| Passwords | password:, passwd=, pwd: followed by a value |
| Tokens | token or bearer followed by a 20+ char value |
| API keys | api_key, api-key, secret_key with a value |
Matched content is skipped before it ever reaches the database.