Documentation
¶
Overview ¶
S77-T08: doctor --deep check for orphaned Hasura remote schemas.
A remote schema is "orphaned" when it is registered in Hasura metadata but the plugin that provides it is no longer installed. This can happen after `nself plugin uninstall <name>` if Hasura metadata was not cleaned up, or after a manual plugin removal. Orphaned remote schemas cause Hasura to emit schema-load errors on startup and break the GraphQL API surface.
Detection strategy:
- Read NSELF_HASURA_GRAPHQL_URL (or derive from HASURA_GRAPHQL_URL) to locate the running Hasura instance.
- Call the Hasura Metadata API to export remote_schemas (POST /v1/metadata with {"type":"export_metadata","args":{}}). Requires HASURA_GRAPHQL_ADMIN_SECRET.
- For each remote schema entry, derive the expected plugin name from the remote schema's URL: internal plugin URLs follow the pattern http://host:PORT where PORT is registered in PLUGIN_<NAME>_INTERNAL_URL.
- Cross-reference against loaded plugins: env var NSELF_PLUGINS_LOADED (comma- separated list set by `nself build`) and per-plugin PLUGIN_<NAME>_INTERNAL_URL.
- Emit WARN for each orphan found; PASS if none; SKIP if Hasura is unreachable.
The check is read-only — it never modifies Hasura metadata. S77-T08
S69-T05: doctor check for ai+moderation wiring on public-bound deployments.
If the ai plugin is loaded AND the deployment binding is non-loopback AND the moderation plugin is NOT loaded, this emits a WARN. Severity is WARN (not FAIL) because self-hosted single-user setups are legitimate.
Package doctor provides comprehensive health check sections for nself doctor.
Index ¶
- Constants
- func RecommendedPoolSize(postgresMaxConnections, numServices int) int
- func RecordCITokenRotation(projectDir string) error
- type CheckResult
- func BackupChecks(_ context.Context, projectDir string) []CheckResult
- func CheckAdminBind(ctx context.Context) CheckResult
- func CheckCIToken(projectDir string) CheckResult
- func CheckCIVaultSync(projectDir string) CheckResult
- func CheckEncryptionKeyScope(projectDir string, strict bool) []CheckResult
- func CheckHasuraIntrospection(_ context.Context) CheckResult
- func CheckHasuraMetadataBackup(backupDir string) CheckResult
- func CheckJWTRotation(projectDir string) CheckResult
- func CheckJWTSecretPresent(projectDir string) CheckResult
- func CheckModerationWired(_ context.Context) CheckResult
- func CheckOrphanRemoteSchemas(ctx context.Context) CheckResult
- func CheckRLSEnforcement(ctx context.Context, strict bool) []CheckResult
- func CheckSDKVersions(ctx context.Context) []CheckResult
- func CheckSSRF(projectDir string) CheckResult
- func DeepChecks(ctx context.Context, projectDir string, verbose bool) []CheckResult
- func DockerDeepChecks(ctx context.Context, verbose bool) []CheckResult
- func DogfoodChecks(ctx context.Context, projectDir string, verbose bool) []CheckResult
- func FilterBySection(results []CheckResult, section string) []CheckResult
- func FixItEngine(ctx context.Context, results []CheckResult) []CheckResult
- func HasuraChecks(ctx context.Context, verbose bool) []CheckResult
- func HostChecks(ctx context.Context, verbose bool) []CheckResult
- func LicenseChecks(ctx context.Context, projectDir string, verbose bool) []CheckResult
- func MonitoringChecks(ctx context.Context) []CheckResult
- func NginxChecks(ctx context.Context, verbose bool) []CheckResult
- func PingChecks(ctx context.Context, verbose bool) []CheckResult
- func PluginHealthChecks(ctx context.Context, projectDir string, verbose bool) []CheckResult
- func PoolCapacityCheck(_ context.Context, pools []PoolConfig, postgresMaxConnections int) CheckResult
- func PostgresChecks(ctx context.Context, verbose bool) []CheckResult
- func SSLChecks(ctx context.Context, verbose bool) []CheckResult
- func SecurityChecks(ctx context.Context, projectDir string) []CheckResult
- func SystemChecks(ctx context.Context, verbose bool) []CheckResult
- type PoolConfig
Constants ¶
const ( // DefaultPostgresMaxConnections is the Postgres default max_connections setting. // Operators should raise this in POSTGRES_MAX_CONNECTIONS when running many services. DefaultPostgresMaxConnections = 100 // PoolCapCheckID is the canonical check ID referenced in docs and Alertmanager. PoolCapCheckID = "PERF-POOL-01" )
const (
// RLSCheckID is the canonical check ID referenced in docs, wiki, and Alertmanager.
RLSCheckID = "PERM-RLS-01"
)
Variables ¶
This section is empty.
Functions ¶
func RecommendedPoolSize ¶ added in v1.0.11
RecommendedPoolSize computes the recommended per-service MaxConns cap.
MaxConns = min(10, floor(postgres_max_connections / num_services))
Edge cases:
- numServices=0 → returns 10 (no services, no constraint)
- very large max_connections → capped at 10 (prevents runaway allocations)
- numServices > max_connections → returns 1 (absolute minimum)
func RecordCITokenRotation ¶ added in v1.0.14
RecordCITokenRotation writes a new timestamp to the rotation log. Called by `nself doctor --record-ci-token-rotation`. Creates parent dirs as needed.
Types ¶
type CheckResult ¶
type CheckResult struct {
Section string `json:"section"`
Name string `json:"name"`
Status string `json:"status"` // pass, warn, fail
Message string `json:"message"`
FixCmd string `json:"fix_cmd,omitempty"` // suggested fix command
}
CheckResult holds the outcome of a single diagnostic check.
func BackupChecks ¶
func BackupChecks(_ context.Context, projectDir string) []CheckResult
BackupChecks verifies backup health.
func CheckAdminBind ¶ added in v1.0.11
func CheckAdminBind(ctx context.Context) CheckResult
CheckAdminBind verifies that the nself-admin container is bound to 127.0.0.1 only (not 0.0.0.0 which would expose it to the LAN). This implements S43-T03 — required by the Service Binding Hard Rule.
func CheckCIToken ¶ added in v1.0.14
func CheckCIToken(projectDir string) CheckResult
CheckCIToken is the CI-TOKEN-01 deep doctor check.
It verifies:
- NSELF_CI_TOKEN is set (in any env cascade file or the environment itself).
- A rotation log exists documenting the last time the token was rotated.
- Expiry-proximity check (primary): if an "expires <RFC3339>" entry exists in the rotation log, warn at ≤14d remaining and fail at ≤1d remaining.
- Rotation-age check (fallback): when no expiry date is present in the log (legacy entries), fall back to the old 90d soft / 120d hard rotation-age logic.
This check is --deep only. It does NOT attempt to call the GitHub API (no network) — it only inspects the local rotation log.
To update the rotation log after a token rotation:
nself doctor --record-ci-token-rotation [--expires <RFC3339>]
(CLI command delegates to internal/doctor.RecordCITokenRotation.)
func CheckCIVaultSync ¶ added in v1.0.14
func CheckCIVaultSync(projectDir string) CheckResult
CheckCIVaultSync is the CI-VAULT-SYNC-01 deep doctor check.
It verifies that vault.env or the live environment contains all critical CI PATs (fail on missing) and 180-day infra tokens (warn on missing).
The check does NOT validate token values — only presence. A token could be present but expired; CI-TOKEN-01 handles rotation-age tracking for NSELF_CI_TOKEN specifically. CI-VAULT-SYNC-01 covers the broader vault coverage question.
func CheckEncryptionKeyScope ¶ added in v1.0.11
func CheckEncryptionKeyScope(projectDir string, strict bool) []CheckResult
CheckEncryptionKeyScope verifies that AI_ENCRYPTION_KEY (and any other *_ENCRYPTION_KEY env vars) are set and are not reused across environments. Implements S43-UNDEP-01 + S43-AUDIT-01 (SEC-ENC-01 and SEC-ENC-02).
When strict=true, a shared or default key returns Status="fail". When strict=false (default), it returns Status="warn" — allowing the admin to investigate without blocking a deploy.
func CheckHasuraIntrospection ¶ added in v1.0.11
func CheckHasuraIntrospection(_ context.Context) CheckResult
CheckHasuraIntrospection implements SEC-HASURA-INTRO-01. It warns if HASURA_GRAPHQL_DISABLE_INTROSPECTION is not set to "true" in a production environment (NSELF_ENV=production or NODE_ENV=production).
The check is:
- status "skip": environment is dev/staging or NSELF_ENV is unset/empty
- status "pass": HASURA_GRAPHQL_DISABLE_INTROSPECTION=true in production
- status "warn": HASURA_GRAPHQL_DISABLE_INTROSPECTION absent or not "true" in production
The check is read-only — it never modifies Hasura configuration. S70-T06
func CheckHasuraMetadataBackup ¶ added in v1.0.14
func CheckHasuraMetadataBackup(backupDir string) CheckResult
CheckHasuraMetadataBackup verifies that a hasura-metadata-*.json file exists in backupDir and is less than 36 hours old (implements BACKUP-METADATA-01).
func CheckJWTRotation ¶ added in v1.0.14
func CheckJWTRotation(projectDir string) CheckResult
CheckJWTRotation implements the JWT-ROT-01 deep doctor check. It verifies:
- HASURA_GRAPHQL_JWT_SECRET is set (delegates to CheckJWTSecretPresent for the precise fail/warn/pass logic on where the key lives).
- A rotation log exists at the configured path.
- The last recorded rotation is within the configured window (default 90d).
This check is only included in --deep mode.
func CheckJWTSecretPresent ¶ added in v1.0.7
func CheckJWTSecretPresent(projectDir string) CheckResult
CheckJWTSecretPresent reports whether HASURA_GRAPHQL_JWT_SECRET is defined in any of the project's env files. Semantics:
- fail when the key is absent from both .env and .env.secrets and no other env file has it either; this is what the spec calls "Hasura enabled but JWT secret absent from both sources".
- warn when the key is in .env.secrets but missing from .env. This is the nSelf-preferred arrangement (secrets live in .env.secrets which is gitignored and chmod 0600), so it is informational only.
- pass when the key is present in .env (with or without .env.secrets).
projectDir is the working directory passed through from `nself doctor`.
func CheckModerationWired ¶ added in v1.0.11
func CheckModerationWired(_ context.Context) CheckResult
CheckModerationWired implements S69-T05: warns when the ai plugin is loaded on a public-bound deployment without the moderation plugin also loaded.
Detection logic:
- ai plugin loaded: NSELF_AI_LOADED=1 OR PLUGIN_AI_INTERNAL_URL non-empty
- moderation plugin loaded: NSELF_MODERATION_LOADED=1 OR PLUGIN_MODERATION_INTERNAL_URL non-empty
- public bind: NSELF_BIND_ADDRESS is NOT 127.x / localhost / ::1 / "" (empty = loopback default)
func CheckOrphanRemoteSchemas ¶ added in v1.0.11
func CheckOrphanRemoteSchemas(ctx context.Context) CheckResult
CheckOrphanRemoteSchemas implements S77-T08. It queries Hasura metadata and compares registered remote schemas against the currently installed nself plugins. Any remote schema whose providing plugin is absent is flagged as an orphan.
func CheckRLSEnforcement ¶ added in v1.0.11
func CheckRLSEnforcement(ctx context.Context, strict bool) []CheckResult
CheckRLSEnforcement implements PERM-RLS-01.
It queries Postgres pg_class for every np_* table and verifies:
(a) RLS is enabled (relrowsecurity = true) (b) at least one policy exists (c) PatternTenantScoped tables have FORCE RLS (relforcerowsecurity = true) (d) tables with tenant_id column have Hasura row filter for that column
Violations are WARN by default. Pass strict=true to escalate to fail.
This check is read-only. It never modifies DB state.
func CheckSDKVersions ¶ added in v1.0.14
func CheckSDKVersions(ctx context.Context) []CheckResult
CheckSDKVersions implements the SDK-VERSION-01 deep doctor check. It returns one CheckResult per SDK (4 total), each independently pass / warn / fail so a single drifted SDK does not mask the others.
func CheckSSRF ¶ added in v1.0.14
func CheckSSRF(projectDir string) CheckResult
CheckSSRF implements the SSRF-01 deep doctor check. It locates the plugins-pro paid/ directory relative to projectDir and verifies that each plugin service carries effective SSRF guard code (file present + guard symbol present).
projectDir is the nself project working directory (where .env lives). plugins-pro is expected to be a sibling of cli/ inside the nself monorepo.
func DeepChecks ¶
func DeepChecks(ctx context.Context, projectDir string, verbose bool) []CheckResult
DeepChecks runs all 12 subsystem categories for --deep mode.
func DockerDeepChecks ¶
func DockerDeepChecks(ctx context.Context, verbose bool) []CheckResult
DockerDeepChecks verifies daemon, storage driver, dangling images, container health.
func DogfoodChecks ¶ added in v1.0.13
func DogfoodChecks(ctx context.Context, projectDir string, verbose bool) []CheckResult
DogfoodChecks runs the full dogfood-only check set. It is invoked from DeepChecks when NSELF_DOGFOOD=1 is set, AND can be invoked directly from CI to gate nself.org's own deploys.
Check IDs (cross-referenced in operations/dogfood-checks.md):
SEC-CSP-01 — CSP headers present on all subapps SEC-HSTS-01 — HSTS preload value SEC-AUTH-01 — HttpOnly cookie convention enforced VENDOR-DEP-01 — no AGPL/SSPL deps in production source DOGFOOD-SUBAPPS-01 — every shipped subapp has synced sport.json DOGFOOD-HEX-01 — no hex colors in subapp src/ trees DOGFOOD-LICENSE-01 — owner license vault path readable
PERM-RLS-01 is intentionally NOT duplicated here; rls_check.go owns it.
func FilterBySection ¶
func FilterBySection(results []CheckResult, section string) []CheckResult
FilterBySection returns only checks matching the given section name.
func FixItEngine ¶
func FixItEngine(ctx context.Context, results []CheckResult) []CheckResult
FixItEngine runs safe auto-fixes for check results that have FixCmd set.
func HasuraChecks ¶
func HasuraChecks(ctx context.Context, verbose bool) []CheckResult
HasuraChecks verifies /healthz 200 and metadata consistency.
func HostChecks ¶
func HostChecks(ctx context.Context, verbose bool) []CheckResult
HostChecks verifies disk, memory, CPU, time sync, kernel.
func LicenseChecks ¶
func LicenseChecks(ctx context.Context, projectDir string, verbose bool) []CheckResult
LicenseChecks verifies license is current and tier matches expected.
func MonitoringChecks ¶
func MonitoringChecks(ctx context.Context) []CheckResult
MonitoringChecks verifies monitoring stack health.
func NginxChecks ¶
func NginxChecks(ctx context.Context, verbose bool) []CheckResult
NginxChecks verifies config test and SSL cert expiry.
func PingChecks ¶
func PingChecks(ctx context.Context, verbose bool) []CheckResult
PingChecks verifies ping.nself.org reachable and license cache fresh.
func PluginHealthChecks ¶
func PluginHealthChecks(ctx context.Context, projectDir string, verbose bool) []CheckResult
PluginHealthChecks verifies every installed plugin's health endpoint.
func PoolCapacityCheck ¶ added in v1.0.11
func PoolCapacityCheck(_ context.Context, pools []PoolConfig, postgresMaxConnections int) CheckResult
PoolCapacityCheck verifies that the total configured pgxpool connections across all active services do not exceed postgres_max_connections.
Returns a CheckResult with:
- Status "pass" when total_conns ≤ postgres_max_connections
- Status "warn" when total_conns > postgres_max_connections (not a hard block)
- Status "fail" when inputs are invalid (zero services, negative max)
This check fires in nself doctor --deep as PERF-POOL-01.
func PostgresChecks ¶
func PostgresChecks(ctx context.Context, verbose bool) []CheckResult
PostgresChecks verifies pg_isready, replication lag, longest query, dead tuples, vacuum.
func SSLChecks ¶
func SSLChecks(ctx context.Context, verbose bool) []CheckResult
SSLChecks verifies LE renewal cron, last renewal, OCSP stapling.
func SecurityChecks ¶
func SecurityChecks(ctx context.Context, projectDir string) []CheckResult
SecurityChecks runs security diagnostics.
func SystemChecks ¶
func SystemChecks(ctx context.Context, verbose bool) []CheckResult
SystemChecks runs system-level diagnostics: disk, memory, swap, load, clock sync, Docker, kernel.
type PoolConfig ¶ added in v1.0.11
type PoolConfig struct {
ServiceName string
MaxConns int // per-service pgxpool MaxConns (0 = use pgxpoolDefaultMaxConns)
}
PoolConfig describes the pool sizing input for a single service. Each plugin or required service exposes one pool config.
func DefaultPoolConfigs ¶ added in v1.0.11
func DefaultPoolConfigs(services []string) []PoolConfig
DefaultPoolConfigs returns a pool config slice for the standard nSelf service set (4 required + up to 7 optional). Pass only the enabled services. Used by nself doctor --deep to compute the PERF-POOL-01 check.