docs: track the coding guidelines and finish tree hygiene
Some checks failed
Deploy Website / deploy (push) Successful in 2m7s
Publish Edge Images / images (apps/console/Dockerfile, barkstack) (push) Successful in 12m9s
Publish Edge Images / images (services/pawsql/Dockerfile, pawsql) (push) Successful in 13m7s
Test and Release Barkstack / test (push) Failing after 2m32s
Verify Platform / verify (push) Failing after 48s
Verify Platform / images (apps/console/Dockerfile, console) (push) Has been skipped
Verify Platform / images (services/pawsql/Dockerfile, pawsql) (push) Has been skipped
Verify Platform / images (services/treatvault/Dockerfile, treatvault) (push) Has been skipped
Test and Release Barkstack / release (push) Has been skipped
Publish Edge Images / images (services/treatvault/Dockerfile, treatvault) (push) Successful in 8m50s
Some checks failed
Deploy Website / deploy (push) Successful in 2m7s
Publish Edge Images / images (apps/console/Dockerfile, barkstack) (push) Successful in 12m9s
Publish Edge Images / images (services/pawsql/Dockerfile, pawsql) (push) Successful in 13m7s
Test and Release Barkstack / test (push) Failing after 2m32s
Verify Platform / verify (push) Failing after 48s
Verify Platform / images (apps/console/Dockerfile, console) (push) Has been skipped
Verify Platform / images (services/pawsql/Dockerfile, pawsql) (push) Has been skipped
Verify Platform / images (services/treatvault/Dockerfile, treatvault) (push) Has been skipped
Test and Release Barkstack / release (push) Has been skipped
Publish Edge Images / images (services/treatvault/Dockerfile, treatvault) (push) Successful in 8m50s
AGENT.md now points at .ai/guidelines, so the guidelines move into the repository and the reference resolves on a fresh clone. Completes the exported doc comments in the console manifest and registry left from the lint pass, and ignores local scratch files (.DS_Store, sdkscratch). The root Barkfile's include files (Barkfile.campbellwireless, Barkfile.ruckstack) stay local on purpose: they are personal infrastructure and Barkfile.campbellwireless currently does not parse (a treatvault block inside a project block, which the parser rejects).
This commit is contained in:
250
.ai/guidelines/01-barkstack-conventions.md
Normal file
250
.ai/guidelines/01-barkstack-conventions.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# Barkstack Conventions
|
||||
|
||||
Barkstack is a self-hosted server stack intended for homelabbers. The idea is to provide something akin to AWS, Azure or GCP but on a much smaller scale for homelabbers.
|
||||
|
||||
The intent is that the platform is modular, that the user can enable and disable features at will and that we make the best use of resources as possible.
|
||||
|
||||
## 1. Portability
|
||||
|
||||
Barkstack should model infrastructure in terms of Barkstack concepts rather than the implementation details of the tools used underneath it.
|
||||
|
||||
Where Barkstack relies on external systems such as Docker, Docker Swarm, CoreDNS, ACME providers, or storage backends, access to those systems should be defined through stable interfaces with concrete provider implementations.
|
||||
|
||||
Runtime-specific types and assumptions should not leak into the core Barkstack resource model.
|
||||
|
||||
For example, an application should be described as a Barkstack workload rather than a Docker service, and a network should be described in terms of the capabilities Barkstack requires rather than as a Docker overlay network.
|
||||
|
||||
This should allow Barkstack to support alternative implementations in the future without requiring the higher-level resource model or Barkfiles to be redesigned.
|
||||
|
||||
Provider-specific functionality may be exposed where useful, but it should be treated as an explicit extension rather than the foundation of the platform.
|
||||
|
||||
## 2. Modularity
|
||||
|
||||
A user should be able to run only the functionality they need. For example, Barkstack must not require PawSQL, TreatVault, DigDNS, CollarCA, or any other optional module simply in order for the core platform to operate.
|
||||
|
||||
Modules should communicate through well-defined Barkstack contracts rather than depending directly on another module's internal implementation.
|
||||
|
||||
Disabling or removing a module should only disable the capabilities provided by that module and features that explicitly depend on it. It should not unnecessarily prevent unrelated parts of Barkstack from operating.
|
||||
|
||||
Modules should own their implementation, API, lifecycle, and management UI while integrating into the common Barkstack control plane and console.
|
||||
|
||||
## 3. Reproducability
|
||||
|
||||
A Barkstack installation should be reproducible from its declarative configuration and backed-up user data.
|
||||
|
||||
The Barkfile is the source of truth for user-controlled desired state. Resources and configuration must not exist solely as hidden state inside the Barkstack database, web console, or runtime.
|
||||
|
||||
Changes made through the CLI, API, or management console should ultimately be reflected in the same declarative resource model represented by the Barkfile.
|
||||
|
||||
Barkstack should clearly separate:
|
||||
|
||||
Desired configuration — what the user wants to exist, represented by the Barkfile.
|
||||
Runtime state — information Barkstack discovers or records about the running system, such as IDs, placement, health, status, and timestamps.
|
||||
User data — persistent application data, database contents, volumes, secrets, certificates, and other state that cannot be recreated purely from configuration.
|
||||
|
||||
A user should be able to move Barkstack to a new system by restoring their Barkfile and required user data, then allowing Barkstack to reconcile the new environment back to the desired state.
|
||||
|
||||
Barkstack should provide or integrate with mechanisms for backing up and restoring user data separately from configuration.
|
||||
|
||||
## 4. Declarative Desired State
|
||||
|
||||
Barkstack configuration describes the state the user wants, not a sequence of actions to execute.
|
||||
|
||||
Components should reconcile the current state of the system against the desired state expressed in the Barkfile.
|
||||
|
||||
Running the same configuration repeatedly should be safe and idempotent.
|
||||
|
||||
Where possible, Barkstack should be able to explain the difference between current and desired state before applying changes.
|
||||
|
||||
The parser, resource model, planning/reconciliation logic, and runtime implementation should remain separate concerns.
|
||||
|
||||
## 5. No Hidden Configuration
|
||||
|
||||
The Barkfile is the source of truth for user-controlled configuration.
|
||||
|
||||
The management UI, CLI, and APIs must not create configuration that exists only inside Barkstack's internal database.
|
||||
|
||||
Changes made through the UI should ultimately result in the same declarative configuration that could have been written by hand.
|
||||
|
||||
Internal state may record runtime information such as resource IDs, health, placement, timestamps, or observed state, but must not silently become an alternative source of desired configuration.
|
||||
|
||||
## 6. Module Independence
|
||||
|
||||
Modules must not unnecessarily depend on other Barkstack modules.
|
||||
|
||||
For example, PawSQL should not require DigDNS, CollarCA or TreatVault unless a feature explicitly uses them.
|
||||
|
||||
Modules should interact through stable Barkstack contracts rather than importing or depending directly on another module's implementation.
|
||||
|
||||
Removing or disabling one module should degrade related functionality cleanly rather than prevent Barkstack from operating.
|
||||
|
||||
## 7. Project Isolation
|
||||
|
||||
Projects are the normal namespace and isolation boundary for user workloads and resources.
|
||||
|
||||
Resources belonging to one project must not implicitly gain access to resources belonging to another project.
|
||||
|
||||
Cross-project communication must be explicit.
|
||||
|
||||
Cluster-level Barkstack services may bridge project boundaries only where required by their function.
|
||||
|
||||
Isolation should be enforced using the strongest practical mechanism provided by the underlying runtime, such as separate Docker Swarm overlay networks.
|
||||
|
||||
## 8. Least Privilege
|
||||
|
||||
Every Barkstack component should receive only the permissions, secrets, networks, volumes and runtime access required to perform its function.
|
||||
|
||||
Access to privileged facilities such as the Docker socket, Swarm manager API, certificate authority keys or secrets stores should be restricted to the smallest possible number of components.
|
||||
|
||||
A module should request an operation from the Barkstack control plane rather than receiving broad infrastructure access whenever practical.
|
||||
|
||||
Secrets must never be passed through command-line arguments, logs or ordinary environment variables when a more appropriate secret mechanism exists.
|
||||
|
||||
## 9. Secure Defaults
|
||||
|
||||
The default configuration should favour isolation and safety.
|
||||
|
||||
Examples include:
|
||||
|
||||
- project networks should not be attachable unless requested
|
||||
- services should not publish ports unless explicitly exposed
|
||||
- secrets should only be attached to services that reference them
|
||||
- databases should not expose their native ports merely for internal connectivity
|
||||
- unknown routes, hosts or resources should fail closed rather than fall back to a default
|
||||
|
||||
Users may explicitly relax these restrictions when necessary.
|
||||
|
||||
## 10. Resource Efficiency
|
||||
|
||||
Barkstack is intended for homelabs where CPU, RAM and storage may be limited.
|
||||
|
||||
Modules should avoid permanently running processes when they can safely be suspended, shared or started on demand.
|
||||
|
||||
Prefer one cluster-level service where appropriate rather than unnecessary per-project copies.
|
||||
|
||||
Resource usage should be observable, and background functionality should justify the resources it consumes.
|
||||
|
||||
Efficiency must not come at the expense of data integrity.
|
||||
|
||||
## 11. Control Plane vs Workloads
|
||||
|
||||
Barkstack's control plane and the workloads it manages should remain conceptually separate.
|
||||
|
||||
The Barkstack controller must not depend on a user workload that it is itself responsible for managing in order to become operational.
|
||||
|
||||
Bootstrap-critical functionality should have minimal dependencies.
|
||||
|
||||
Avoid circular dependencies such as Barkstack requiring a PawSQL-managed database before PawSQL can be started by Barkstack.
|
||||
|
||||
## 12. Stable Resource Identity
|
||||
|
||||
Resources should have stable identities independent of their current runtime placement.
|
||||
|
||||
Applications should refer to logical resources rather than physical nodes, container IDs or IP addresses wherever possible.
|
||||
|
||||
Moving a database, application or service between nodes should not require dependent resources to be reconfigured.
|
||||
|
||||
Friendly names may be scoped by project, but internally Barkstack should be able to uniquely identify resources without ambiguity.
|
||||
|
||||
## 13. Runtime Details Are Implementation Details
|
||||
|
||||
The Barkfile should describe user intent rather than leaking unnecessary details of Docker, Swarm, Caddy or other runtime technologies.
|
||||
|
||||
For example, users should normally describe:
|
||||
|
||||
visibility public
|
||||
|
||||
rather than:
|
||||
|
||||
publish_mode ingress
|
||||
|
||||
and:
|
||||
|
||||
project foo
|
||||
|
||||
rather than manually constructing overlay networks.
|
||||
|
||||
Runtime-specific escape hatches may exist for advanced users, but they should not define the core Barkstack resource model.
|
||||
|
||||
## 14. Graceful Degradation
|
||||
|
||||
The failure or absence of one module should not unnecessarily take down the rest of Barkstack.
|
||||
|
||||
If PawSQL is unavailable, the Barkstack console, DNS management and certificate management should remain usable.
|
||||
|
||||
The UI and CLI should clearly distinguish between:
|
||||
|
||||
- unavailable
|
||||
- degraded
|
||||
- unhealthy
|
||||
- intentionally disabled
|
||||
|
||||
Failures should be visible and actionable rather than silently ignored.
|
||||
|
||||
## 15. Data Safety
|
||||
|
||||
Barkstack must never sacrifice user data for convenience or automatic recovery.
|
||||
|
||||
If Barkstack cannot safely determine where persistent data resides, it should fail rather than start a service against an empty or incorrect volume.
|
||||
|
||||
Destructive operations should be explicit.
|
||||
|
||||
Persistent data lifecycle must be separate from workload lifecycle; deleting or recreating a container or service must not implicitly delete its data.
|
||||
|
||||
Backup and restore should be considered part of the resource lifecycle, not an afterthought.
|
||||
|
||||
## 16. Observable Behaviour
|
||||
|
||||
Barkstack should make it possible to understand what it is doing and why.
|
||||
|
||||
Important operations should produce structured logs and useful events.
|
||||
|
||||
Where appropriate, resources should expose:
|
||||
|
||||
- desired state
|
||||
- observed state
|
||||
- health
|
||||
- current placement
|
||||
- dependencies
|
||||
- last meaningful event
|
||||
- errors preventing reconciliation
|
||||
|
||||
Users should not need to inspect Docker internals for normal troubleshooting.
|
||||
|
||||
## 17. Predictability Over Magic
|
||||
|
||||
Automatic behaviour should be deterministic and explainable.
|
||||
|
||||
Barkstack may choose placement, generate names, provision networks, issue certificates or suspend resources automatically, but the rules used to make those decisions should be understandable and inspectable.
|
||||
|
||||
Avoid behaviour that changes depending on undocumented heuristics.
|
||||
|
||||
If Barkstack makes a decision on behalf of the user, the UI and CLI should make that decision visible.
|
||||
|
||||
## 18. Backwards-Compatible Evolution
|
||||
|
||||
Barkfiles are user-owned infrastructure definitions and should be treated as a stable public interface.
|
||||
|
||||
Avoid breaking syntax or semantics unnecessarily.
|
||||
|
||||
When a breaking change is unavoidable, provide a clear migration path and useful validation errors.
|
||||
|
||||
Configuration formats and module APIs should be versioned where doing so protects future compatibility.
|
||||
|
||||
## 19. One Resource Model, Multiple Interfaces
|
||||
|
||||
The Barkfile, CLI, web console and APIs should operate against the same underlying Barkstack resource model.
|
||||
|
||||
Do not implement separate business logic for each interface.
|
||||
|
||||
A database created through the web console should represent the same resource as one declared through the Barkfile.
|
||||
|
||||
The UI is a frontend to Barkstack, not a second control plane.
|
||||
|
||||
## 20. Self-Hosted First
|
||||
|
||||
Barkstack must not require a third-party cloud service to provide its core functionality.
|
||||
|
||||
External services may be supported as optional providers, but there should be a fully self-hosted path for core functionality wherever reasonably possible.
|
||||
|
||||
Barkstack should continue to function on an isolated LAN except for features that inherently require external connectivity.
|
||||
513
.ai/guidelines/02-golang-coding-conventions.md
Normal file
513
.ai/guidelines/02-golang-coding-conventions.md
Normal file
@@ -0,0 +1,513 @@
|
||||
# Go Coding Conventions
|
||||
|
||||
Barkstack Go code should favour clarity, correctness, maintainability, and idiomatic Go over abstraction or cleverness.
|
||||
|
||||
Code should be easy for another developer to understand without needing to reconstruct the author's intent.
|
||||
|
||||
## 1. General Style
|
||||
|
||||
All Go code must be formatted with `gofmt`.
|
||||
|
||||
Prefer standard, idiomatic Go patterns over custom frameworks or abstractions.
|
||||
|
||||
Use the Go standard library where it provides a reasonable solution. Introduce third-party dependencies only when they provide meaningful value.
|
||||
|
||||
Prefer simple code that is easy to delete, replace, or refactor.
|
||||
|
||||
Avoid speculative abstractions for functionality that does not yet exist.
|
||||
|
||||
Do not introduce design patterns simply because they are common in other languages.
|
||||
|
||||
## 2. Packages
|
||||
|
||||
Packages should have a clear and narrow responsibility.
|
||||
|
||||
Prefer package names that describe what the package provides:
|
||||
|
||||
```text
|
||||
config
|
||||
router
|
||||
runtime
|
||||
projects
|
||||
secrets
|
||||
network
|
||||
```
|
||||
|
||||
Avoid vague package names such as:
|
||||
|
||||
```text
|
||||
utils
|
||||
helpers
|
||||
common
|
||||
misc
|
||||
manager
|
||||
```
|
||||
|
||||
unless the name genuinely describes a coherent domain concept.
|
||||
|
||||
Avoid circular dependencies.
|
||||
|
||||
Higher-level Barkstack domain packages must not depend directly on implementation-specific packages where an abstraction boundary exists.
|
||||
|
||||
For example:
|
||||
|
||||
```text
|
||||
projects
|
||||
↓
|
||||
runtime.Runtime
|
||||
↓
|
||||
runtime/swarm
|
||||
```
|
||||
|
||||
is preferable to:
|
||||
|
||||
```text
|
||||
projects
|
||||
↓
|
||||
docker.Client
|
||||
```
|
||||
|
||||
## 3. Interfaces
|
||||
|
||||
Interfaces should represent meaningful behavioural boundaries.
|
||||
|
||||
Prefer defining interfaces where they are consumed rather than creating large provider interfaces pre-emptively.
|
||||
|
||||
Interfaces should normally be small.
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
type SecretStore interface {
|
||||
Get(ctx context.Context, name string) ([]byte, error)
|
||||
}
|
||||
```
|
||||
|
||||
is preferable to one large interface exposing every possible secrets operation.
|
||||
|
||||
Do not create an interface solely to make every struct mockable.
|
||||
|
||||
Use concrete types where no abstraction boundary is required.
|
||||
|
||||
Implementation-specific types such as Docker, Swarm, CoreDNS, or provider SDK types should not leak through Barkstack domain interfaces.
|
||||
|
||||
## 4. Types and Resource Models
|
||||
|
||||
Use explicit domain types for important Barkstack concepts.
|
||||
|
||||
Prefer:
|
||||
|
||||
```go
|
||||
type ProjectID string
|
||||
type ResourceID string
|
||||
```
|
||||
|
||||
where stronger typing helps prevent accidental misuse.
|
||||
|
||||
Avoid representing structured domain data as untyped maps such as:
|
||||
|
||||
```go
|
||||
map[string]any
|
||||
```
|
||||
|
||||
unless working at a serialization or compatibility boundary.
|
||||
|
||||
Resource models should distinguish between:
|
||||
|
||||
* desired state
|
||||
* observed/runtime state
|
||||
* provider-specific state
|
||||
|
||||
Do not mix all three into one large struct without a clear reason.
|
||||
|
||||
## 5. Functions
|
||||
|
||||
Functions should do one understandable job.
|
||||
|
||||
Prefer short functions with descriptive names over long functions containing multiple phases of behaviour.
|
||||
|
||||
If a function requires extensive comments explaining its control flow, consider splitting it.
|
||||
|
||||
Avoid boolean parameters whose meaning is unclear:
|
||||
|
||||
```go
|
||||
createThing(true, false)
|
||||
```
|
||||
|
||||
Prefer explicit options or separate methods.
|
||||
|
||||
Return early where doing so reduces nesting.
|
||||
|
||||
Prefer:
|
||||
|
||||
```go
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !enabled {
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
over deeply nested conditionals.
|
||||
|
||||
## 6. Errors
|
||||
|
||||
Errors must be handled explicitly.
|
||||
|
||||
Do not silently discard errors unless there is a documented reason.
|
||||
|
||||
Wrap errors with useful context using `%w`:
|
||||
|
||||
```go
|
||||
return fmt.Errorf("create project network %q: %w", name, err)
|
||||
```
|
||||
|
||||
Error messages should explain what operation failed and identify the relevant resource where useful.
|
||||
|
||||
Avoid redundant wording such as:
|
||||
|
||||
```text
|
||||
error: failed to...
|
||||
```
|
||||
|
||||
when the caller already knows it received an error.
|
||||
|
||||
Use sentinel errors or typed errors when callers need to distinguish classes of failure programmatically.
|
||||
|
||||
Do not inspect error strings to determine behaviour.
|
||||
|
||||
Expected user-facing validation failures should be distinguishable from unexpected internal failures.
|
||||
|
||||
## 7. Context
|
||||
|
||||
Operations involving I/O, networking, subprocesses, Docker/Swarm APIs, databases, or potentially long-running work should accept `context.Context`.
|
||||
|
||||
`context.Context` should normally be the first function parameter:
|
||||
|
||||
```go
|
||||
func Resolve(ctx context.Context, name string) (Resource, error)
|
||||
```
|
||||
|
||||
Do not store contexts inside long-lived structs.
|
||||
|
||||
Respect cancellation and deadlines.
|
||||
|
||||
Where appropriate, apply explicit timeouts around external systems rather than allowing operations to wait indefinitely.
|
||||
|
||||
## 8. Concurrency
|
||||
|
||||
Do not introduce concurrency unless it provides a meaningful benefit.
|
||||
|
||||
Concurrency must have a clear ownership and cancellation model.
|
||||
|
||||
Every goroutine should have an understandable lifetime.
|
||||
|
||||
Avoid goroutines that can outlive the component that created them unless intentionally managed as background workers.
|
||||
|
||||
Use contexts, channels, `sync.WaitGroup`, `errgroup`, or equivalent mechanisms to coordinate shutdown.
|
||||
|
||||
Avoid shared mutable state where message passing or immutable snapshots are simpler.
|
||||
|
||||
Run tests with the race detector where concurrency is involved:
|
||||
|
||||
```bash
|
||||
go test -race ./...
|
||||
```
|
||||
|
||||
## 9. Resource Cleanup
|
||||
|
||||
Resources must be released deterministically.
|
||||
|
||||
Use `defer` for cleanup where appropriate:
|
||||
|
||||
```go
|
||||
conn, err := net.Dial(...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
```
|
||||
|
||||
Pay particular attention to:
|
||||
|
||||
* network connections
|
||||
* HTTP response bodies
|
||||
* files
|
||||
* temporary directories
|
||||
* goroutines
|
||||
* timers
|
||||
* Docker/API clients
|
||||
|
||||
Do not leak resources on error paths.
|
||||
|
||||
## 10. Configuration Parsing and Validation
|
||||
|
||||
Parsing and validation should be separate concerns.
|
||||
|
||||
The parser should answer:
|
||||
|
||||
> What configuration did the user write?
|
||||
|
||||
Validation should answer:
|
||||
|
||||
> Is this configuration valid?
|
||||
|
||||
Runtime reconciliation should answer:
|
||||
|
||||
> How do we make the system match this configuration?
|
||||
|
||||
Avoid performing Docker, filesystem, network, or other runtime operations while parsing the Barkfile.
|
||||
|
||||
Validation errors should include useful source context such as:
|
||||
|
||||
* filename
|
||||
* line number
|
||||
* resource name
|
||||
* field name
|
||||
|
||||
where available.
|
||||
|
||||
Prefer collecting multiple independent validation failures when doing so helps the user fix configuration efficiently.
|
||||
|
||||
## 11. Reconciliation
|
||||
|
||||
Reconciliation logic should be idempotent.
|
||||
|
||||
Running reconciliation repeatedly against an already-correct system should result in no destructive or unnecessary changes.
|
||||
|
||||
Prefer:
|
||||
|
||||
```text
|
||||
observe
|
||||
compare
|
||||
plan
|
||||
apply
|
||||
```
|
||||
|
||||
over blindly issuing create/update commands.
|
||||
|
||||
Reconciliation should clearly distinguish:
|
||||
|
||||
* resource does not exist
|
||||
* resource already matches desired state
|
||||
* resource requires modification
|
||||
* resource cannot safely be reconciled
|
||||
|
||||
Never destroy or recreate persistent resources merely because doing so is easier to implement.
|
||||
|
||||
## 12. Logging
|
||||
|
||||
Use structured logging.
|
||||
|
||||
Prefer `log/slog` unless the project has deliberately standardized on another logger.
|
||||
|
||||
Logs should contain useful fields:
|
||||
|
||||
```go
|
||||
logger.Info(
|
||||
"database started",
|
||||
"project", projectName,
|
||||
"database", databaseName,
|
||||
"node", nodeName,
|
||||
)
|
||||
```
|
||||
|
||||
Avoid encoding structured information into formatted message strings.
|
||||
|
||||
Do not log:
|
||||
|
||||
* passwords
|
||||
* private keys
|
||||
* secret contents
|
||||
* authentication tokens
|
||||
* full connection strings containing credentials
|
||||
|
||||
Use appropriate log levels consistently.
|
||||
|
||||
Expected user mistakes should generally not produce alarming internal error logs.
|
||||
|
||||
## 13. Naming
|
||||
|
||||
Use clear, descriptive names.
|
||||
|
||||
Avoid abbreviations unless they are broadly understood within the domain.
|
||||
|
||||
Prefer:
|
||||
|
||||
```go
|
||||
database
|
||||
project
|
||||
hostname
|
||||
certificate
|
||||
```
|
||||
|
||||
over:
|
||||
|
||||
```go
|
||||
db
|
||||
proj
|
||||
host
|
||||
cert
|
||||
```
|
||||
|
||||
when the longer name improves readability.
|
||||
|
||||
Short variable names are appropriate for very small scopes where their meaning is obvious.
|
||||
|
||||
Exported identifiers must have meaningful names and should form readable API surfaces.
|
||||
|
||||
## 14. Comments and Documentation
|
||||
|
||||
Comments should explain why something exists or why a non-obvious decision was made.
|
||||
|
||||
Do not write comments that simply repeat the code.
|
||||
|
||||
Prefer:
|
||||
|
||||
```go
|
||||
// Keep the database pinned to this node because its volume is node-local.
|
||||
```
|
||||
|
||||
over:
|
||||
|
||||
```go
|
||||
// Set node ID.
|
||||
```
|
||||
|
||||
Exported public APIs should have Go documentation comments where appropriate.
|
||||
|
||||
Important architectural constraints should be documented close to the relevant interface or implementation.
|
||||
|
||||
## 15. Dependency Injection
|
||||
|
||||
Dependencies should be explicit.
|
||||
|
||||
Prefer constructor injection:
|
||||
|
||||
```go
|
||||
func NewController(runtime Runtime, secrets SecretStore) *Controller
|
||||
```
|
||||
|
||||
over hidden global state or package-level mutable variables.
|
||||
|
||||
Avoid service locator patterns.
|
||||
|
||||
Components should be testable without requiring a live Docker daemon, DNS server, or Swarm cluster unless the test is explicitly an integration test.
|
||||
|
||||
## 16. Global State
|
||||
|
||||
Avoid mutable global state.
|
||||
|
||||
Constants, immutable lookup tables, embedded static files, and similar read-only data are acceptable.
|
||||
|
||||
Runtime clients, configuration, loggers, caches, and mutable registries should normally belong to explicitly constructed objects.
|
||||
|
||||
## 17. Testing
|
||||
|
||||
New behaviour must include tests appropriate to its risk.
|
||||
|
||||
Use table-driven tests where they improve clarity.
|
||||
|
||||
Test observable behaviour rather than implementation details.
|
||||
|
||||
Unit tests should cover:
|
||||
|
||||
* normal operation
|
||||
* invalid input
|
||||
* boundary conditions
|
||||
* important error paths
|
||||
|
||||
Use `httptest`, temporary directories, fake implementations, and in-memory resources where appropriate.
|
||||
|
||||
Integration tests should be used for behaviour that depends on real external systems such as:
|
||||
|
||||
* Docker Swarm
|
||||
* PostgreSQL
|
||||
* DNS
|
||||
* filesystem behaviour
|
||||
* certificate issuance
|
||||
|
||||
Tests must not depend on execution order.
|
||||
|
||||
Tests should clean up resources they create.
|
||||
|
||||
## 18. Test Fixtures and Mocks
|
||||
|
||||
Prefer lightweight fakes or small test implementations over large generated mocks where practical.
|
||||
|
||||
A fake should implement the same public interface as the production provider.
|
||||
|
||||
Do not make production interfaces artificially large simply to support mocking.
|
||||
|
||||
Shared test fixtures should remain readable and should not obscure what each test is actually proving.
|
||||
|
||||
## 19. Validation Before Completion
|
||||
|
||||
Before considering Go changes complete, run at minimum:
|
||||
|
||||
```bash
|
||||
gofmt -w .
|
||||
go test ./...
|
||||
go vet ./...
|
||||
```
|
||||
|
||||
Where concurrency is involved:
|
||||
|
||||
```bash
|
||||
go test -race ./...
|
||||
```
|
||||
|
||||
Run any project-configured linters as well.
|
||||
|
||||
The repository should compile cleanly and tests should pass before work is considered finished.
|
||||
|
||||
Do not leave known lint, test, or compilation failures for later.
|
||||
|
||||
## 20. Backwards Compatibility
|
||||
|
||||
Treat public Go interfaces, Barkfile parsing behaviour, APIs, and persisted resource formats as compatibility surfaces.
|
||||
|
||||
Avoid breaking existing behaviour without a concrete reason.
|
||||
|
||||
When changing public behaviour:
|
||||
|
||||
* update tests
|
||||
* update documentation
|
||||
* provide migration handling where necessary
|
||||
|
||||
Do not silently reinterpret existing configuration.
|
||||
|
||||
## 21. Security
|
||||
|
||||
Treat all external input as untrusted, including:
|
||||
|
||||
* Barkfiles
|
||||
* API requests
|
||||
* DNS names
|
||||
* resource names
|
||||
* provider responses
|
||||
* filesystem paths
|
||||
|
||||
Validate input at boundaries.
|
||||
|
||||
Avoid shelling out where a native Go API exists.
|
||||
|
||||
Where subprocess execution is necessary, never build commands by concatenating untrusted strings.
|
||||
|
||||
Use explicit permissions when creating files containing sensitive information.
|
||||
|
||||
Security-sensitive operations should fail closed.
|
||||
|
||||
## 22. Performance
|
||||
|
||||
Optimize only where there is evidence that optimization matters.
|
||||
|
||||
Avoid unnecessary polling, background workers, allocations, or repeated API calls.
|
||||
|
||||
Cache external state only when there is a clear invalidation model.
|
||||
|
||||
For homelab use, reducing persistent CPU and memory consumption is generally more valuable than maximizing throughput.
|
||||
|
||||
Correctness and data safety take priority over performance.
|
||||
630
.ai/guidelines/03-svelte-coding-conventions.md
Normal file
630
.ai/guidelines/03-svelte-coding-conventions.md
Normal file
@@ -0,0 +1,630 @@
|
||||
# Svelte Coding Conventions
|
||||
|
||||
Barkstack frontend code should be simple, strongly typed, accessible, modular, and consistent across the core console and independently shipped module UIs.
|
||||
|
||||
Use Svelte 5 patterns and TypeScript for application code.
|
||||
|
||||
The UI should favour clarity and predictable behaviour over clever abstractions or excessive frontend framework machinery.
|
||||
|
||||
## 1. TypeScript
|
||||
|
||||
Use TypeScript for Svelte components and frontend application code.
|
||||
|
||||
Prefer explicit types for:
|
||||
|
||||
* API responses
|
||||
* resource models
|
||||
* component props
|
||||
* event payloads
|
||||
* configuration objects
|
||||
|
||||
Avoid `any`.
|
||||
|
||||
Where data originates outside the frontend's trust boundary, validate or narrow it before treating it as a trusted domain type.
|
||||
|
||||
Do not duplicate backend models blindly if the frontend only requires a smaller view model.
|
||||
|
||||
## 2. Svelte 5
|
||||
|
||||
Use modern Svelte 5 patterns.
|
||||
|
||||
Prefer runes such as:
|
||||
|
||||
```text
|
||||
$state
|
||||
$derived
|
||||
$effect
|
||||
```
|
||||
|
||||
where reactive state is required.
|
||||
|
||||
Do not introduce legacy reactive syntax into new code unless required by compatibility with existing code.
|
||||
|
||||
Use `$effect` for actual side effects, not as a substitute for derived state.
|
||||
|
||||
Prefer `$derived` when a value can be calculated directly from other state.
|
||||
|
||||
Keep reactive data flow understandable.
|
||||
|
||||
## 3. Components
|
||||
|
||||
Components should have one clear responsibility.
|
||||
|
||||
Prefer composition of small components over very large page components.
|
||||
|
||||
A component that simultaneously:
|
||||
|
||||
* fetches data
|
||||
* manages routing
|
||||
* contains multiple forms
|
||||
* renders several unrelated panels
|
||||
* handles dialogs
|
||||
|
||||
should usually be decomposed.
|
||||
|
||||
Do not extract tiny components merely for the sake of having more files.
|
||||
|
||||
Create a component when it provides a meaningful reusable or conceptual boundary.
|
||||
|
||||
## 4. Props
|
||||
|
||||
Component inputs should be explicit and strongly typed.
|
||||
|
||||
Prefer a clear props interface over passing large unstructured objects.
|
||||
|
||||
Do not mutate props owned by a parent.
|
||||
|
||||
Use callbacks or application events for actions that belong to the parent.
|
||||
|
||||
Default values should be obvious and predictable.
|
||||
|
||||
## 5. State Ownership
|
||||
|
||||
State should live at the narrowest scope that needs it.
|
||||
|
||||
Prefer:
|
||||
|
||||
```text
|
||||
component-local state
|
||||
```
|
||||
|
||||
over:
|
||||
|
||||
```text
|
||||
page state
|
||||
```
|
||||
|
||||
and page state over:
|
||||
|
||||
```text
|
||||
global state
|
||||
```
|
||||
|
||||
unless multiple unrelated areas genuinely need to share the state.
|
||||
|
||||
Avoid global stores for ordinary component state.
|
||||
|
||||
Do not create global state merely to avoid passing a small number of props.
|
||||
|
||||
## 6. Derived State
|
||||
|
||||
Do not store data that can be reliably derived from existing state.
|
||||
|
||||
Prefer:
|
||||
|
||||
```ts
|
||||
let filtered = $derived(items.filter(...));
|
||||
```
|
||||
|
||||
over maintaining both:
|
||||
|
||||
```text
|
||||
items
|
||||
filteredItems
|
||||
```
|
||||
|
||||
through manual synchronization.
|
||||
|
||||
Duplicated state is a common source of frontend bugs.
|
||||
|
||||
## 7. Effects
|
||||
|
||||
Effects should be used sparingly.
|
||||
|
||||
An `$effect` should normally interact with something outside normal declarative state flow, such as:
|
||||
|
||||
* browser APIs
|
||||
* subscriptions
|
||||
* timers
|
||||
* imperative third-party libraries
|
||||
|
||||
Do not use effects merely to copy one reactive value into another.
|
||||
|
||||
Effects that allocate resources must clean them up.
|
||||
|
||||
## 8. API Access
|
||||
|
||||
Centralize HTTP/API behaviour rather than scattering raw `fetch` calls throughout components.
|
||||
|
||||
Prefer:
|
||||
|
||||
```text
|
||||
API client
|
||||
↓
|
||||
page/component
|
||||
```
|
||||
|
||||
over embedding URL construction, error parsing, authentication handling, and response decoding in every component.
|
||||
|
||||
API calls should:
|
||||
|
||||
* handle non-success responses
|
||||
* expose meaningful typed errors
|
||||
* support cancellation where appropriate
|
||||
* avoid silently swallowing failures
|
||||
|
||||
Use relative API URLs so Barkstack remains independent of its public hostname or reverse-proxy configuration.
|
||||
|
||||
## 9. Loading, Empty and Error States
|
||||
|
||||
Every asynchronous screen should deliberately handle:
|
||||
|
||||
* loading
|
||||
* success
|
||||
* empty
|
||||
* error
|
||||
|
||||
Do not leave a blank screen while loading.
|
||||
|
||||
Do not treat an empty resource list as an error.
|
||||
|
||||
Errors should explain what failed and, where practical, what the user can do next.
|
||||
|
||||
Avoid exposing raw stack traces or backend implementation details to ordinary users.
|
||||
|
||||
## 10. Routing
|
||||
|
||||
The Barkstack shell owns global browser routing.
|
||||
|
||||
Module UIs should operate within the route namespace assigned to them.
|
||||
|
||||
A module must not assume control over the entire browser application.
|
||||
|
||||
For plugin/module navigation, use the Barkstack navigation contract rather than directly replacing global application state.
|
||||
|
||||
Deep links, browser refresh, back, and forward navigation should work.
|
||||
|
||||
Do not encode critical application state solely in transient component memory when it belongs in the URL.
|
||||
|
||||
## 11. Barkstack Module Boundaries
|
||||
|
||||
A module such as PawSQL, DigDNS, CollarCA, or TreatVault owns its module-specific UI.
|
||||
|
||||
The Barkstack core console owns shared application chrome such as:
|
||||
|
||||
* sidebar
|
||||
* global header
|
||||
* routing
|
||||
* breadcrumbs
|
||||
* notifications
|
||||
* confirmation dialogs
|
||||
* global theme
|
||||
|
||||
Module UIs must not recreate these elements.
|
||||
|
||||
Module UI code should not be copied into the Barkstack core repository merely for convenience.
|
||||
|
||||
Keep the plugin contract independent of a specific module.
|
||||
|
||||
## 12. Custom Elements
|
||||
|
||||
Where Barkstack modules are loaded as custom elements, treat the custom-element boundary as a public compatibility interface.
|
||||
|
||||
Properties and emitted events should be:
|
||||
|
||||
* documented
|
||||
* typed
|
||||
* minimal
|
||||
* backwards compatible where practical
|
||||
|
||||
Use namespaced events such as:
|
||||
|
||||
```text
|
||||
barkstack:navigate
|
||||
barkstack:notify
|
||||
barkstack:confirm
|
||||
```
|
||||
|
||||
Do not rely on undocumented access to the host application's internal Svelte state.
|
||||
|
||||
## 13. Styling
|
||||
|
||||
Use the Barkstack design language consistently.
|
||||
|
||||
Shared visual values should come from CSS custom properties/design tokens such as:
|
||||
|
||||
```text
|
||||
--bark-bg
|
||||
--bark-surface
|
||||
--bark-border
|
||||
--bark-text
|
||||
--bark-text-muted
|
||||
--bark-primary
|
||||
--bark-danger
|
||||
--bark-radius-md
|
||||
```
|
||||
|
||||
Avoid hard-coding slightly different versions of the same colours and spacing throughout modules.
|
||||
|
||||
Prefer flat, restrained styling consistent with Barkstack's visual identity.
|
||||
|
||||
Avoid unnecessary gradients, excessive animation, heavy shadows, or decorative effects that reduce clarity.
|
||||
|
||||
Module-specific styling should not leak outside the module.
|
||||
|
||||
## 14. CSS
|
||||
|
||||
Prefer component-scoped CSS.
|
||||
|
||||
Keep selectors simple.
|
||||
|
||||
Avoid excessive specificity and `!important`.
|
||||
|
||||
Do not use arbitrary global selectors from module code.
|
||||
|
||||
Use semantic class names based on purpose rather than visual appearance where practical.
|
||||
|
||||
Prefer layout primitives such as:
|
||||
|
||||
* flexbox
|
||||
* grid
|
||||
* gap
|
||||
|
||||
over brittle margin chains and absolute positioning.
|
||||
|
||||
## 15. Accessibility
|
||||
|
||||
Accessibility is part of correctness.
|
||||
|
||||
Use semantic HTML.
|
||||
|
||||
Prefer:
|
||||
|
||||
```html
|
||||
<button>
|
||||
```
|
||||
|
||||
over clickable `<div>` elements.
|
||||
|
||||
Controls must be usable by keyboard.
|
||||
|
||||
Form inputs must have labels.
|
||||
|
||||
Icons used as controls require accessible names.
|
||||
|
||||
Focus states must remain visible.
|
||||
|
||||
Use ARIA only where native HTML semantics are insufficient.
|
||||
|
||||
Dialogs must manage focus appropriately.
|
||||
|
||||
Colour must not be the only indicator of status.
|
||||
|
||||
## 16. Forms
|
||||
|
||||
Use semantic form controls.
|
||||
|
||||
Validation should occur at useful times and provide actionable messages.
|
||||
|
||||
Backend validation remains authoritative.
|
||||
|
||||
Do not rely solely on frontend validation for security or correctness.
|
||||
|
||||
Avoid clearing user-entered form state after a recoverable submission failure.
|
||||
|
||||
Destructive operations should require deliberate user action and appropriate confirmation.
|
||||
|
||||
## 17. Tables and Resource Lists
|
||||
|
||||
Infrastructure UIs frequently contain tables. Keep them readable and useful.
|
||||
|
||||
Resource tables should normally support sensible representations of:
|
||||
|
||||
* resource name
|
||||
* status
|
||||
* project
|
||||
* important metadata
|
||||
* actions
|
||||
|
||||
Avoid filling tables with every field available from the API.
|
||||
|
||||
Use detail pages for secondary information.
|
||||
|
||||
Loading and empty states should not cause major layout shifts.
|
||||
|
||||
## 18. Status Representation
|
||||
|
||||
Use a consistent vocabulary and visual representation for resource states.
|
||||
|
||||
Examples might include:
|
||||
|
||||
```text
|
||||
healthy
|
||||
degraded
|
||||
unavailable
|
||||
stopped
|
||||
starting
|
||||
suspended
|
||||
unknown
|
||||
```
|
||||
|
||||
Do not invent module-specific synonyms where an existing Barkstack status already describes the condition.
|
||||
|
||||
Status should never be communicated through colour alone.
|
||||
|
||||
## 19. Icons
|
||||
|
||||
Use a consistent icon library or Barkstack-provided icon set.
|
||||
|
||||
Do not mix several unrelated icon styles.
|
||||
|
||||
Icons should support comprehension rather than act as decoration everywhere.
|
||||
|
||||
Module manifests may refer to semantic icon names rather than embedding arbitrary HTML or SVG where possible.
|
||||
|
||||
## 20. User Feedback
|
||||
|
||||
User-triggered operations must produce feedback.
|
||||
|
||||
For successful actions, use the shared Barkstack notification mechanism where appropriate.
|
||||
|
||||
For failures, show enough information to understand what went wrong.
|
||||
|
||||
Long-running operations should indicate progress or ongoing state.
|
||||
|
||||
Do not leave buttons appearing active after an operation has completed or failed.
|
||||
|
||||
## 21. Confirmation Dialogs
|
||||
|
||||
Use confirmation dialogs for genuinely destructive or high-impact actions.
|
||||
|
||||
Do not ask for confirmation for ordinary reversible actions.
|
||||
|
||||
Confirmation wording should describe the specific consequence:
|
||||
|
||||
```text
|
||||
Delete database "photos"?
|
||||
|
||||
This removes the database service. Its volume will be retained.
|
||||
```
|
||||
|
||||
rather than:
|
||||
|
||||
```text
|
||||
Are you sure?
|
||||
```
|
||||
|
||||
Use the Barkstack shell's shared confirmation mechanism rather than implementing different dialogs in each module.
|
||||
|
||||
## 22. Browser APIs
|
||||
|
||||
Access browser-only globals such as:
|
||||
|
||||
```text
|
||||
window
|
||||
document
|
||||
localStorage
|
||||
navigator
|
||||
```
|
||||
|
||||
only where appropriate.
|
||||
|
||||
Keep browser API usage behind clear boundaries so components remain testable.
|
||||
|
||||
Do not use local storage as a hidden source of infrastructure configuration.
|
||||
|
||||
Local storage may be used for local presentation preferences such as dismissed hints or UI state where appropriate.
|
||||
|
||||
## 23. Performance
|
||||
|
||||
Do not optimize prematurely.
|
||||
|
||||
Avoid unnecessary repeated requests and expensive reactive computations.
|
||||
|
||||
Large lists should not trigger complete application rerenders for unrelated state changes.
|
||||
|
||||
Lazy-load module UI bundles and expensive views where it provides a meaningful benefit.
|
||||
|
||||
Do not introduce complicated caching without a clear invalidation strategy.
|
||||
|
||||
## 24. Dependencies
|
||||
|
||||
Prefer the platform and Svelte itself before adding third-party frontend dependencies.
|
||||
|
||||
Dependencies should solve a real problem rather than save a few lines of straightforward code.
|
||||
|
||||
Avoid large UI frameworks that undermine Barkstack's design system or unnecessarily inflate independently shipped module bundles.
|
||||
|
||||
Keep module dependency trees reasonably small.
|
||||
|
||||
## 25. Testing
|
||||
|
||||
Frontend behaviour should be tested at the appropriate level.
|
||||
|
||||
Unit/component tests should cover important behaviours such as:
|
||||
|
||||
* route selection
|
||||
* form validation
|
||||
* loading/error/empty states
|
||||
* emitted plugin events
|
||||
* conditional rendering
|
||||
* resource status display
|
||||
|
||||
Prefer testing what the user can observe rather than internal implementation details.
|
||||
|
||||
Use browser/end-to-end tests for critical integration flows where component tests cannot provide enough confidence.
|
||||
|
||||
Do not mock so much of the system that tests cease to represent real behaviour.
|
||||
|
||||
## 26. Validation Before Completion
|
||||
|
||||
Before frontend work is considered complete, run the project-configured equivalents of:
|
||||
|
||||
```bash
|
||||
svelte-check
|
||||
eslint
|
||||
prettier --check
|
||||
```
|
||||
|
||||
and the frontend test suite.
|
||||
|
||||
Run a production build as part of validation.
|
||||
|
||||
A change is not complete if it:
|
||||
|
||||
* fails TypeScript checking
|
||||
* produces Svelte compiler warnings that should be fixed
|
||||
* fails linting
|
||||
* fails tests
|
||||
* fails the production build
|
||||
|
||||
Do not leave known warnings or failures for later unless they are explicitly documented and accepted.
|
||||
|
||||
## 27. Console Errors
|
||||
|
||||
The browser console should remain clean during normal usage.
|
||||
|
||||
Do not knowingly ship:
|
||||
|
||||
* uncaught promise rejections
|
||||
* failed resource requests
|
||||
* Svelte warnings
|
||||
* repeated network failures
|
||||
* accessibility warnings
|
||||
|
||||
Expected backend failures should be handled as application states rather than appearing as unexplained console exceptions.
|
||||
|
||||
## 28. Comments
|
||||
|
||||
Comments should explain intent, constraints, or non-obvious browser/framework behaviour.
|
||||
|
||||
Do not comment obvious markup.
|
||||
|
||||
Document unusual workarounds and link them conceptually to the constraint requiring them so future developers know whether they can safely remove them.
|
||||
|
||||
## 29. Security
|
||||
|
||||
Do not render unsanitized HTML from API responses.
|
||||
|
||||
Avoid `{@html}` unless the source and sanitization strategy are explicitly understood.
|
||||
|
||||
Do not put secrets, tokens, private keys, or sensitive credentials into:
|
||||
|
||||
* frontend bundles
|
||||
* browser logs
|
||||
* local storage
|
||||
* URLs
|
||||
* DOM attributes
|
||||
|
||||
The frontend should assume that anything shipped to the browser is observable by the user.
|
||||
|
||||
Authorization must be enforced by the backend even if the UI hides unavailable actions.
|
||||
|
||||
## 30. Keep the UI Boring in the Best Way
|
||||
|
||||
Barkstack is infrastructure software.
|
||||
|
||||
The interface should prioritize:
|
||||
|
||||
* clarity
|
||||
* speed
|
||||
* density where useful
|
||||
* consistent interactions
|
||||
* obvious status
|
||||
* predictable navigation
|
||||
|
||||
over novelty.
|
||||
|
||||
Animations and visual flourishes should support understanding rather than distract from it.
|
||||
|
||||
A user should be able to understand the state of their homelab quickly without fighting the interface.
|
||||
|
||||
## 31. Shared UX Components
|
||||
|
||||
Barkstack should favour reusable UX components over bespoke UI implementations.
|
||||
|
||||
Common interaction patterns and visual elements should be implemented once in the shared Barkstack UI library and reused across the core console and module UIs.
|
||||
|
||||
Examples include:
|
||||
|
||||
buttons
|
||||
form controls
|
||||
selects
|
||||
toggles
|
||||
checkboxes
|
||||
badges
|
||||
status indicators
|
||||
cards
|
||||
tables
|
||||
empty states
|
||||
loading states
|
||||
error states
|
||||
dialogs
|
||||
confirmation prompts
|
||||
notifications
|
||||
page headers
|
||||
breadcrumbs
|
||||
tabs
|
||||
code blocks
|
||||
resource summaries
|
||||
metric cards
|
||||
action menus
|
||||
pagination
|
||||
search/filter controls
|
||||
|
||||
Before implementing a new UI pattern inside a module, first determine whether an equivalent shared component already exists.
|
||||
|
||||
If the pattern is likely to be useful elsewhere in Barkstack, prefer adding or extending a shared component rather than implementing a module-specific version.
|
||||
|
||||
Module-specific components are appropriate when they represent genuinely domain-specific behaviour, such as a PawSQL database connection panel or a DigDNS zone editor.
|
||||
|
||||
Shared components should remain domain-neutral.
|
||||
|
||||
Prefer:
|
||||
|
||||
Barkstack UI library
|
||||
├── Button
|
||||
├── DataTable
|
||||
├── StatusBadge
|
||||
├── ConfirmDialog
|
||||
├── EmptyState
|
||||
└── ResourceHeader
|
||||
|
||||
with modules composing them:
|
||||
|
||||
PawSQL
|
||||
└── DatabaseList
|
||||
├── ResourceHeader
|
||||
├── DataTable
|
||||
└── StatusBadge
|
||||
|
||||
rather than each module independently creating its own table, badge, buttons, dialogs and loading states.
|
||||
|
||||
Shared components should encapsulate:
|
||||
|
||||
consistent styling
|
||||
accessibility behaviour
|
||||
keyboard interaction
|
||||
focus management
|
||||
validation presentation
|
||||
loading and disabled states
|
||||
Barkstack design tokens
|
||||
common interaction conventions
|
||||
|
||||
Do not duplicate shared components merely to make small visual changes.
|
||||
|
||||
Extend the shared component API where the requirement is broadly useful.
|
||||
|
||||
Do not turn the shared UI library into a collection of module-specific business logic.
|
||||
|
||||
The goal is for PawSQL, DigDNS, CollarCA, TreatVault, and future modules to feel like parts of one product rather than independently designed applications.
|
||||
0
.ai/guidelines/04-e2e-testing-guideline.md
Normal file
0
.ai/guidelines/04-e2e-testing-guideline.md
Normal file
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,3 +7,5 @@ apps/website/build/
|
||||
apps/console/server/dist/
|
||||
services/pawsql/internal/adminui/dist/
|
||||
services/treatvault/internal/adminui/dist/
|
||||
.DS_Store
|
||||
sdkscratch/
|
||||
|
||||
10
AGENT.md
10
AGENT.md
@@ -11,3 +11,13 @@ Every pull request that changes user-visible behavior **MUST** update the releva
|
||||
- Use concrete commands and state their assumptions, especially Swarm-manager requirements, bind-mounted files, network exposure, Docker-socket access, authentication, and TLS boundaries.
|
||||
- Remove or revise documentation for behavior that a pull request replaces. Do not leave deprecated flags, images, or workflows documented.
|
||||
- Run `bun install && bun run build` from `apps/website` after changing the site. The build must continue to emit a fully static `build/` directory.
|
||||
|
||||
## Coding Guidelines
|
||||
|
||||
Before planning, designing, implementing, reviewing, or modifying code, read the applicable guidelines in `.ai/guidelines`.
|
||||
|
||||
All plans, designs, implementations, tests, and code reviews must comply with those guidelines.
|
||||
|
||||
Where multiple guidelines apply, follow all of them. If a proposed approach conflicts with a guideline, change the approach rather than ignoring the guideline.
|
||||
|
||||
Do not consider work complete until it has been validated according to the applicable guidelines.
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PluginAPIVersion is the manifest API version this console server accepts.
|
||||
const PluginAPIVersion = "barkstack.dev/ui/v1"
|
||||
|
||||
var (
|
||||
@@ -15,17 +16,22 @@ var (
|
||||
elementPattern = regexp.MustCompile(`^[a-z][a-z0-9]*(?:-[a-z0-9]+)+$`)
|
||||
)
|
||||
|
||||
// PluginEndpoint is one statically configured plugin backend.
|
||||
type PluginEndpoint struct {
|
||||
ID string
|
||||
BaseURL string
|
||||
}
|
||||
|
||||
// NavigationItem is one navigation entry a plugin contributes to the
|
||||
// console sidebar.
|
||||
type NavigationItem struct {
|
||||
Label string `json:"label"`
|
||||
Path string `json:"path"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
}
|
||||
|
||||
// PluginManifest is the manifest document a plugin serves at its manifest
|
||||
// endpoint.
|
||||
type PluginManifest struct {
|
||||
APIVersion string `json:"apiVersion"`
|
||||
ID string `json:"id"`
|
||||
@@ -40,6 +46,8 @@ type PluginManifest struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// ValidateManifest checks a fetched manifest against the Barkfile-configured
|
||||
// plugin ID and the naming rules for mounts, entries, and elements.
|
||||
func ValidateManifest(configuredID string, manifest PluginManifest) error {
|
||||
if manifest.APIVersion != PluginAPIVersion {
|
||||
return fmt.Errorf("unsupported API version %q", manifest.APIVersion)
|
||||
|
||||
@@ -29,6 +29,8 @@ type refreshCall struct {
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// Registry caches plugin manifests for the configured endpoints and hands
|
||||
// out validated base URLs and display-ready manifest snapshots.
|
||||
type Registry struct {
|
||||
endpoints map[string]PluginEndpoint
|
||||
baseURLs map[string]*url.URL
|
||||
@@ -41,6 +43,8 @@ type Registry struct {
|
||||
refreshes map[string]*refreshCall
|
||||
}
|
||||
|
||||
// NewRegistry validates the configured endpoints and returns a ready
|
||||
// Registry with empty manifest state.
|
||||
func NewRegistry(endpoints []PluginEndpoint, logger *slog.Logger) (*Registry, error) {
|
||||
// A missing logger would silently drop refresh failures; require one.
|
||||
if logger == nil {
|
||||
@@ -72,6 +76,9 @@ func NewRegistry(endpoints []PluginEndpoint, logger *slog.Logger) (*Registry, er
|
||||
return registry, nil
|
||||
}
|
||||
|
||||
// RefreshIfStale refreshes the cached manifests when they are older than
|
||||
// maximumAge. Concurrent callers arriving during a refresh wave share its
|
||||
// result instead of issuing their own requests.
|
||||
func (r *Registry) RefreshIfStale(ctx context.Context, maximumAge time.Duration) {
|
||||
r.mu.Lock()
|
||||
fresh := time.Since(r.lastRefresh) < maximumAge
|
||||
@@ -108,6 +115,8 @@ func (r *Registry) RefreshIfStale(ctx context.Context, maximumAge time.Duration)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
// Refresh fetches every plugin manifest concurrently and republishes the
|
||||
// cache. Unreachable plugins keep their most recent valid manifest.
|
||||
func (r *Registry) Refresh(ctx context.Context) {
|
||||
states := make(map[string]pluginState, len(r.endpoints))
|
||||
var mu sync.Mutex
|
||||
@@ -156,7 +165,7 @@ func (r *Registry) fetchManifest(ctx context.Context, id string, baseURL *url.UR
|
||||
if err != nil {
|
||||
return PluginManifest{}, fmt.Errorf("fetch manifest: %w", err)
|
||||
}
|
||||
defer response.Body.Close()
|
||||
defer func() { _ = response.Body.Close() }() // best-effort: nothing actionable if the close fails
|
||||
if response.StatusCode != http.StatusOK {
|
||||
_, _ = io.Copy(io.Discard, io.LimitReader(response.Body, 4096))
|
||||
return PluginManifest{}, fmt.Errorf("fetch manifest: service returned %s", response.Status)
|
||||
@@ -165,16 +174,18 @@ func (r *Registry) fetchManifest(ctx context.Context, id string, baseURL *url.UR
|
||||
decoder.DisallowUnknownFields()
|
||||
var manifest PluginManifest
|
||||
if err := decoder.Decode(&manifest); err != nil {
|
||||
return PluginManifest{}, fmt.Errorf("%w: decode: %v", errInvalidManifest, err)
|
||||
return PluginManifest{}, fmt.Errorf("%w: decode: %w", errInvalidManifest, err)
|
||||
}
|
||||
manifest.Available = false
|
||||
manifest.Error = ""
|
||||
if err := ValidateManifest(id, manifest); err != nil {
|
||||
return PluginManifest{}, fmt.Errorf("%w: %v", errInvalidManifest, err)
|
||||
return PluginManifest{}, fmt.Errorf("%w: %w", errInvalidManifest, err)
|
||||
}
|
||||
return manifest, nil
|
||||
}
|
||||
|
||||
// Plugins returns the current manifest snapshot ordered by plugin name, with
|
||||
// availability and error text filled in for the UI.
|
||||
func (r *Registry) Plugins() []PluginManifest {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
@@ -211,11 +222,12 @@ func invalidManifest(id string) PluginManifest {
|
||||
return manifest
|
||||
}
|
||||
|
||||
// BaseURL returns a copy of the parsed base URL for the configured plugin ID.
|
||||
func (r *Registry) BaseURL(id string) (*url.URL, bool) {
|
||||
baseURL, ok := r.baseURLs[id]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
copy := *baseURL
|
||||
return ©, true
|
||||
cloned := *baseURL
|
||||
return &cloned, true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user