Repository navigation
Plugin Entitlement
In v1.5 mode (NSELF_V15=1), build and start attempt online validation once per configured key for installed licensed plugins, using the existing timeout client and a short overall deadline. Transport failures, 5xx, and 429 do not discard the existing cache or entitlement. The CLI writes the verified ping licence JWT to .nself/state/entitlement.jwt with mode 0600. It mounts that single file read-only at /run/nself/entitlement.jwt and sets NSELF_ENTITLEMENT_FILE to that container path. Free plugins receive neither the mount nor the variable. The JWT and licence key never appear in generated compose or environment files.
Refresh opens an existing file for writing, writes the token, truncates to its length, and fsyncs the file without changing its inode. A running container's single-file bind mount pins that inode, so replacing it with a rename would leave the container reading a stale token. First creation uses a temporary file and rename followed by a directory fsync. An empty directory left at the target by Docker is removed; a nonempty directory fails with E125. Symlinked targets, .nself, and state directories are refused. Unix targets with multiple hard links are refused before chmod or writing.
On Unix, the bind mount preserves host ownership and mode 0600. The plugin process must run as the file's host owner UID or as a UID with permission to read it, typically container root. Configure the container user accordingly; a different unprivileged UID cannot read the token and receives 503. P7-PLUG-62 covers container readability in its end-to-end gate. On Windows, permissions follow the project directory ACLs; Go's chmod(0600) does not establish owner-only ACLs, so restrict access to the project and state directories. Windows refresh does not inspect hard-link counts and updates every link to the existing file. Windows may not support directory flushing after rename; token bytes are still fsynced before publication.
Build, start, and doctor share the manifest licence classifier: v2 uses license: licensed; v1 also recognizes requires_license, isCommercial, and a nonempty tier other than free. Malformed manifests fail closed with E125 naming the plugin directory. A licensed manifest with category: security is refused under ADR 0005. That category check protects future manifests; manifest validation owns security capabilities without that marker. Licensed compose fragments reject non-core YAML tags on services, each service, environment, and volumes, top-level include, multiple YAML documents, service-level << and extends, and a licensed service name appearing in another discovered fragment, with E125.
Discovery refreshes the runtime file once after scanning licensed plugins. Planning performs no credential write. Applying refreshes the independently changing credential without embedding the JWT in the confirmed compose plan, so it needs no declared plan effect containing token bytes.
If no verified JWT is obtainable, build reports E125 (exit 3), retaining a current token that verifies within grace and belongs to a configured key. Otherwise it publishes an empty file. A rejected key cannot authorize a retained token; another configured key can keep the file populated when its valid token covers every installed licensed plugin. Start continues only for the entitlement refresh error, after the auto-build completes its compose manifest and remaining steps. Invalid licensed fragments and every other build error remain fatal. Start reuses the auto-build's validation result rather than validating twice. license set clears an existing token only on an explicit rejection and when no other configured key supplies a covering token. HTTP 401 is an explicit authentication refusal. HTTP 403 clears only for a signed rejection or ping's JSON error shape (valid: false and a nonempty error); a captive portal's unsigned HTML 403 preserves the existing file. Responses 400/404/429, 5xx, and transport errors also preserve it. Default builds always retain the mount, including when LICENSE_PUBLIC_KEY_OVERRIDE is set. Only a development build tagged nself_devkeys with that override set can skip the mount, with a warning. v1.4 mode performs no entitlement injection. A fragment already normalized in v1.5 retains its persisted mount when switched back to v1.4; byte-identical v1.4 output requires a fresh fragment.
The v1 contract carries one JWT per project. Refresh checks that its plugins claim covers every enabled installed licensed manifest name, or contains *; E125 names uncovered plugins. When online renewal is unavailable, refresh compares the on-disk token and cached token and chooses the covering verified token with the latest signed exp. A rejected key is excluded from that selection. Multiple keys with separate bundles need the Epic-owned v1.1 contract extension before all bundles can serve together. A token beyond signed exp plus thirty days is still published but reports E125, "entitlement grace ended". Run nself license refresh to revalidate.
In v1.5 mode, nself license set outside a project still validates online and updates the global licence cache on success. A transient validation failure prints a warning and the saved confirmation and exits successfully, both outside a project and inside one with licensed plugins. The key remains saved and the next build retries online validation. An explicit rejection remains an error; E125 is reserved for a project whose licensed plugins need entitlement.
The github.com/nself-org/cli/sdk/go/v2/licensing package verifies compact EdDSA JWTs against committed ping public keys and the header's kid. It fails closed for unknown keys, invalid signatures, unsupported algorithms, wrong issuer (ping.nself.org), wrong audience (nself-cli), or a plugin slug absent from plugins (unless "*" is present). Runtime environment overrides and key files cannot change the production verifier's trusted keys.
The SDK rejects symlinked entitlement files as well as non-regular files. Kubernetes Secret and ConfigMap mounts normally expose files through symlinks and therefore fail closed with HTTP 503 when used directly as the entitlement path. The CLI currently emits Compose single-file bind mounts. For Kubernetes, copy the token to a regular file and configure NSELF_ENTITLEMENT_FILE to that file; refresh that copy when the secret changes.
The grace period (EntitlementGracePeriod) ends thirty days after the signed exp, including the endpoint. Local file timestamps and refresh times do not extend grace. When a licensed plugin is installed, doctor reports E126 (exit class 1) for a missing, empty, directory, invalid, or stale entitlement file, including when its verified exp is more than seven days old. Without installed licensed plugins, doctor ignores the file. Unattended refresh scheduling is a separate feature.
The HTTP guard re-reads the file every twenty-four hours and caches the verified expiry and plugin decision on each reload. If a reload encounters a read error or an incomplete JWT, it keeps a previously verified decision through that token's original signed exp plus grace. Subsequent requests retry the read under a mutex at most once every five seconds. Deleting or making the file unreadable after startup therefore does not immediately revoke a verified token; it can only serve until the original signed deadline. Neither retries nor file deletion extend the D8 grace anchor.
A complete JWT with an invalid signature or claims, a token for another plugin, an explicitly empty or oversized file, or an expired signed deadline fails closed immediately. A structurally complete torn token with a bad signature is indistinguishable from a forged token and also fails closed. With no previously verified token, a missing, unreadable, or incomplete file fails closed; unauthorized requests retry at most once per minute. These failures return HTTP 503 with {"error":"entitlement_expired"} on every route except GET and HEAD when URL.EscapedPath() is exactly /health. Percent-encoded variants remain guarded; a query string does not change that path. The refresh worker has an owned lifetime and must be closed with the plugin's server.
Until v1.6.0, an unset NSELF_ENTITLEMENT_FILE permits serving and logs one warning. VerifyEntitlement(slug, now) returns ErrEntitlementUnconfigured for that state; non-HTTP callers must treat it as serve-with-warning until v1.6.0, as Guard(ctx, slug, next, ...GuardOption) already does. This absence rule supports released v1.4.x CLIs, which never set the variable. A configured file requires a verified token; only the bounded cached-decision behavior above tolerates its later disappearance. The compatibility exception sunsets at v1.6.0.
Key rotation must ship the SDK with the next public key first, rebuild and distribute licensed plugin images second, and switch ping to the new signing key last. Keep the old public key while old tokens remain within grace. No replacement key is invented here. TestKeysCompiled() reports builds tagged nself_testkeys; Guard logs one loud warning for these builds. The plugins-pro release gate must inspect go version -m <bin> and refuse a binary whose build tags include nself_testkeys.
Runtime entitlement is best effort on self-hosted code: an operator can modify the guard, and a copied valid token grants at most its remaining signed grace. Plugin code linked into the same binary is trusted code; the committed-key guarantee prevents environment and file overrides, rather than changes by linked code. Licence-gated downloads provide the primary control. Free plugins and security capabilities do not perform entitlement checks.
Getting Started
Commands
- Commands, Overview
- Lifecycle: cmd-init · cmd-build · cmd-start · cmd-stop · cmd-restart · cmd-dev
- Monitoring: cmd-status · cmd-logs · cmd-health · cmd-urls · cmd-doctor · cmd-monitor · cmd-alerts · cmd-sentry · cmd-watchdog
- Data: cmd-db · cmd-backup · cmd-dr · cmd-queue · cmd-webhooks
- Config: cmd-config · cmd-service · cmd-env · cmd-promote
- Networking: cmd-ssl · cmd-trust · cmd-dns-setup
- Security: cmd-access · cmd-security · cmd-secrets
- Tenancy: cmd-tenant · cmd-billing
- Plugins: cmd-plugin · cmd-license · cmd-dogfood (extracted, CLI-R11) · cmd-k8s (extracted, CLI-R11) · cmd-encryption (extracted, CLI-R11) · cmd-waf (extracted, CLI-R11) · cmd-federation (extracted, CLI-R11) · cmd-mail (extracted, CLI-R11) · cmd-dlq (extracted, CLI-R11)
- AI: cmd-ai · cmd-claw · cmd-model
- Templates: cmd-template
- Utilities: cmd-exec · cmd-clean · cmd-reset · cmd-update · cmd-upgrade · cmd-version · cmd-admin · cmd-migrate · cmd-migrate-firebase · cmd-migrate-supabase · cmd-completion
Features
- Features, Overview
- Feature-Auth
- Feature-Storage
- Feature-Search
- Feature-Functions
- Feature-Email
- Feature-Monitoring
- Feature-Plugins
- Feature-nClaw, AI Assistant
- Feature-nChat, Messaging
- Feature-nFamily, Family Social
- Feature-nCloud, Managed Hosting
- Feature-Memory-Rooms, Knowledge Organization
- Feature-Agent-Dashboard, Agent Metrics
- Feature-Image-Generation, AI Image Generation
Configuration
- Configuration, Overview
- Config-Env-Vars
- Config-Postgres
- Config-Hasura
- Config-Auth
- Config-Nginx
- Shared-Proxy
- Config-Optional-Services
- Config-Custom-Services
- Config-System
Plugins (87 + 10 monitoring)
- Plugin-Overview
- Plugin-Catalog
- Plugin-Install
- Plugin-Licensing
- Plugin-Architecture
- Plugin-Dev-Guide
- Plugin-Migrations-Contract
Free (25)
- plugin-backup
- plugin-content-acquisition
- plugin-content-progress
- plugin-cron
- plugin-donorbox
- plugin-feature-flags
- plugin-github
- plugin-github-runner
- plugin-invitations
- plugin-jobs
- plugin-link-preview
- plugin-mdns
- plugin-mlflow
- plugin-monitoring
- plugin-notifications
- plugin-notify
- plugin-paypal
- plugin-search
- plugin-shopify
- plugin-stripe
- plugin-subtitle-manager
- plugin-tokens
- plugin-torrent-manager
- plugin-vpn
- plugin-webhooks
Pro (62)
- plugin-access-controls
- plugin-activity-feed
- plugin-admin-api
- plugin-analytics
- plugin-auth
- plugin-bots
- plugin-browser
- plugin-calendar
- plugin-cdn
- plugin-chat
- plugin-claw
- plugin-claw-budget
- plugin-claw-news
- plugin-claw-web
- plugin-cloudflare
- plugin-cms
- plugin-compliance
- plugin-ddns
- plugin-devices
- plugin-documents
- plugin-entitlements
- plugin-file-processing
- plugin-game-metadata
- plugin-geocoding
- plugin-geolocation
- plugin-google
- plugin-home
- plugin-idme
- plugin-knowledge-base
- plugin-linkedin
- plugin-livekit
- plugin-media-processing
- plugin-meetings
- plugin-moderation
- plugin-mux
- plugin-object-storage
- plugin-observability
- plugin-photos
- plugin-podcast
- plugin-post
- plugin-realtime
- plugin-retro-gaming
- plugin-rom-discovery
- plugin-social
- plugin-sports
- plugin-support
- plugin-tmdb
- plugin-voice
- plugin-web3
- plugin-workflows
Planned (26)
plugin-auditplugin-blogplugin-checkoutplugin-commerceplugin-drmplugin-exportplugin-flowplugin-importplugin-ldapplugin-mailgunplugin-mediaplugin-oauth-providersplugin-pagesplugin-postmarkplugin-rate-limitplugin-reportsplugin-samlplugin-schedulerplugin-sendgridplugin-ssoplugin-subscriptionplugin-thumbplugin-transcoderplugin-twilioplugin-wafplugin-watermark
Guides
- Guide-Production-Deployment
- Guide-SSL-Setup
- Guide-Multi-Tenancy
- Guide-Security-Hardening
- Guide-Monitoring-Setup
- Guide-Backup-Restore
- Guide-Custom-Services
- Guide-Migration-from-v1
Architecture
- Architecture
- Architecture-Guardrails
- Service-Graph
- Compose-Generation
- Nginx-Generation
- Plugin-Architecture
Reference
Machine interface
-
JSON-Output,
--jsonenvelope -
Command-Registry,
nself help --json - Exit-Codes
- error-codes, Error Codes
-
Compat-V15,
NSELF_V15gate
Licensing
Security
Brand
Operations
- operations/release-cascade, Release Cascade
- operations/self-healing, Self-Healing Schema
- operations/redis-tuning, Redis Pool Tuning
- operations/meilisearch-warmup, MeiliSearch Warm-Up
- operations/jwt-rotation, JWT Key Rotation
- operations/windows-wsl2-setup, Windows / WSL2 Setup
- operations/gemini-oauth-reauth, Gemini OAuth Reauth
Contributing
Admin
- USER-ACTION-QUEUE, Pending Admin Actions
All commands (21)
- A: cmd-add · cmd-admin
- B: cmd-backup · cmd-build
- C: cmd-clean · cmd-config
- D: cmd-db · cmd-deploy · cmd-doctor
- E: cmd-exec
- I: cmd-init
- L: cmd-license · cmd-logs
- M: cmd-mcp
- R: cmd-remove · cmd-reset · cmd-restart
- S: cmd-start · cmd-status · cmd-stop
- U: cmd-update