


Add Modules to a Go Server Without Weakening Security
Use when building or reviewing features for an extensible Go server. The AI writes each module in order from creation to cleanup, reads config files strictly, passes tests, lint, and builds on Linux, macOS, and Windows before merging, and leaves pull requests to a human.
To use this AI rule
## Keep secure defaults and decide where each new feature belongs
Treat secure defaults as the primary product requirement for an internet-facing server platform.
Design features as composable modules rather than tightly coupling them to the core.
Prefer general extensions that serve a class of use cases over patches for one deployment.
Identify whether a change belongs to a top-level application, a reusable module, configuration adaptation, or the command-line entry point.
Keep module identifiers namespaced so independently developed components do not collide.
Preserve a native structured configuration model even when human-friendly adapters are available.
Review request handling, transport security, identity, certificate management, and trust boundaries with extra scrutiny.
Trace security-sensitive behavior across related repositories or packages when ownership is distributed.
Do not treat a security-critical change as merge-ready until its affected trust boundaries and failure modes are understood.
Keep the standard module registry explicit so shipped components are discoverable and auditable.
Ensure command binaries import the intended module set rather than relying on accidental registration.
Document architectural placement when a new feature could reasonably live in more than one layer.
Reject designs that weaken secure defaults merely to reduce implementation effort.
Keep compatibility across supported operating systems in view from the first design decision.
## Follow common Go style for errors, names, and comments
Use early returns for errors and keep the successful path at the lowest indentation level.
Avoid else branches after a branch that already returns.
Write common initialisms in their conventional uppercase form, including URL, HTTP, and ID.
Choose short receiver names that consistently reflect the receiver type.
Start error strings with lowercase text and omit trailing punctuation.
Write exported documentation as complete sentences beginning with the declared name.
Use a nil slice when an allocated empty slice has no semantic value.
Return errors for expected failures instead of panicking.
Keep error handling close to the operation that can fail.
Wrap errors only when the additional context helps locate the failed operation.
Preserve errors in a form that callers can inspect when programmatic handling is expected.
Avoid clever control flow that obscures resource ownership or cleanup.
Run formatting and static analysis before review.
Apply the repository's established conventions even when another valid Go style exists.
Use compile-time interface assertions for types expected to satisfy framework interfaces.
Keep receiver, package, and exported names stable unless a deliberate compatibility change is approved.
## Build each module in order, from creation to cleanup
Register each module through the framework's standard registration mechanism.
Return metadata containing a namespaced module ID and a constructor for a fresh instance.
Keep constructors free of environment-specific side effects.
Treat the lifecycle as ordered phases:
1. Construct a new value.
2. Decode configuration into it.
3. Provision runtime dependencies.
4. Validate the completed configuration.
5. Serve or execute the module's function.
6. Clean up owned resources.
Do not perform final validation before configuration and provisioning data are available.
Make cleanup safe after partial provisioning and repeated shutdown signals when the framework can call it that way.
Use compile-time guards for provisioning, validation, configuration parsing, and other optional interfaces.
Keep lifecycle responsibilities separate so configuration errors are distinguishable from runtime failures.
Return actionable errors from provisioning and validation.
Do not hide failed initialization behind a partially usable module.
Use top-level application interfaces only for components that truly own start and stop behavior.
Keep module IDs stable because configuration, adapters, and external integrations may depend on them.
## Read config files strictly and protect admin endpoints
Parse human-facing configuration through the framework's token dispenser or equivalent structured iterator.
Consume the directive name before reading inline arguments.
Handle inline arguments separately from nested blocks.
Capture the current nesting level and stop when the parser leaves that block.
Recognize supported subdirectives explicitly.
Validate required arguments immediately and return the parser's standard argument error.
Reject unknown subdirectives with an error that includes the unexpected value.
Document accepted syntax beside the parser as a maintained contract.
Keep the adapter's output aligned with the native structured configuration model.
Obtain loggers and other modules through the framework context during provisioning.
Use a module-scoped structured logger and attach fields rather than building unstructured messages.
Do not store request or provisioning contexts in long-lived structs.
Implement the framework's routing interface when adding administrative endpoints.
Apply authentication, authorization, and exposure rules expected by the administrative surface.
Validate configuration before the module begins handling traffic.
## Avoid new dependencies and keep package boundaries clear
Avoid new dependencies unless their value clearly exceeds maintenance, security, and supply-chain costs.
Inline small, stable functionality when a tiny dependency would add disproportionate risk.
Do not expose external-package types through the project's public API.
Wrap third-party behavior behind project-owned interfaces and types.
Use the language's module tooling to keep dependency declarations and checksums consistent.
Run the dependency tidy command and review its diff rather than accepting unrelated churn.
Keep configuration adapters separate from runtime modules.
Keep test utilities separate from production entry points.
Place standard modules in the registry used by the shipped command.
Do not import optional modules into the default binary without an explicit product decision.
Preserve package boundaries around protocol handling, transport security, and certificate operations.
Choose generalized abstractions only after identifying the repeated behavior they serve.
Reject speculative abstraction that obscures a simple implementation.
When refactoring, preserve observable behavior or document and test every intentional change.
Review security-critical packages and dependency changes more deeply than routine internal code.
## Pass every test, lint, and build check before merging
Treat every required quality gate as blocking, not advisory.
Run the full short test suite with race detection enabled.
Run the configured linter with enough time for the complete repository.
Build all packages to catch missing interfaces, platform constraints, and registration errors.
For performance changes, run benchmarks with memory measurements.
Compare benchmark results against an appropriate baseline and account for variance.
Run focused integration tests for affected server behavior.
Ensure tests pass on Linux, macOS, and Windows when those platforms are supported by CI.
Inspect warnings in changed files even if unrelated warnings exist elsewhere.
Do not mark a pull request merge-ready while a required test, lint, build, or benchmark gate is missing.
Record commands and relevant results so reviewers can reproduce the evidence.
Distinguish a test failure from an environment failure before changing production code.
Keep verification proportional to the change while still covering every directly affected gate.
Re-run affected gates after any code change made in response to review.
Treat race reports and flaky concurrency failures as defects requiring investigation.
## Write table-driven unit tests and server tests on separate ports
Prefer table-driven tests when several inputs share the same behavior.
Give each case explicit input, expected output, and expected-error state.
Check both missing expected errors and unexpected returned errors.
Compare returned values only after handling the error condition appropriately.
Include valid, invalid, boundary, and regression cases.
Name cases when indexes alone would make failures hard to diagnose.
Keep fixtures minimal and deterministic.
Use the project's server test harness for end-to-end configuration behavior.
Initialize test servers with isolated configuration and explicit formats.
Use non-standard ports to avoid conflicts with locally running services.
Reserve separate ports for administration, plain traffic, and encrypted traffic when needed.
Assert status, body, and other externally observable behavior.
Clean up servers, listeners, files, and goroutines created by the test.
Do not rely on test ordering or shared mutable global state.
Test configuration parsing errors as well as successful server startup.
Add platform-specific cases when paths, sockets, signals, or process behavior differ.
Keep integration tests focused enough that a failure identifies the broken contract.
## Leave pull requests, security reports, and agreements to a human
Treat issue discussions, pull requests, license agreements, and security reports as human-governed external actions.
An AI assistant must not impersonate a human contributor or accept a legal agreement on a person's behalf.
Do not create or submit external issues, pull requests, or security reports unless the governing policy explicitly authorizes that agent action.
Keep review assistance within the permitted role and identify AI participation when required.
Do not conceal AI involvement when disclosure is part of the contribution policy.
Require the human operator to understand and be able to explain every submitted line.
Test AI-assisted code with automated checks where feasible and thorough manual checks otherwise.
Review generated code for copied material, incompatible licensing, and unverified claims.
Do not invent vulnerability details or submit an unverified security report.
Translation or editing assistance must preserve the human author's verified security facts.
Leave human-only signatures, attestations, and contributor agreements to the responsible person.
When a requested action conflicts with contribution policy, stop that action and explain the governing boundary.
Do not add deceptive files, comments, or commits as a substitute for a prohibited external action.
Keep the patch technically useful without bypassing community process.
Before approval, verify disclosure, comprehension, testing, and licensing as separate gates.
Human accountability remains required even when an agent produced most of the implementation.
