Secure a server
This guide locks down a Priompt server. You turn on bearer tokens for the gRPC API, scope tokens to orgs, encrypt connections with TLS or mTLS, and put a token on the NATS broker that carries change events. For how the server decides what a token may do, see Orgs, tokens, and access.
A server started without tokens accepts every request with admin rights, including writes. Use that only on a machine nobody else can reach.
Prerequisites
Before you begin, make sure you have:
- The
priomptbinary: see Install Priompt. - OpenSSL: only to create test certificates for TLS.
Create an admin token
priompt init writes a random admin token with write access to tokens.txt and prints the commands that use it.
To turn on authentication:
-
Create the tokens file:
priompt initOutputwrote admin token to tokens.txtstart server: priompt serve -tokens-file tokens.txtpoint client: export PRIOMPT_URL=priompt://<token>@localhost:8443initrefuses to overwrite an existing file:tokens.txt already exists; pass -force to overwrite. -
Start the server with the file:
priompt serve -tokens-file tokens.txtThe startup line reports how many tokens it loaded, for example
auth=1-token. -
Point clients at the server with the printed URL:
export PRIOMPT_URL=priompt://<token>@localhost:8443The
priomptclient commands and both SDKs readPRIOMPT_URL. The CLI also accepts the token alone inPRIOMPT_TOKEN.
A request without a valid token now fails with Unauthenticated: invalid or missing token.
On the server, PRIOMPT_TOKEN adds an admin token with write access. On a client, it's the token to send. If you export it for the CLI and then start priompt serve in the same shell, that value becomes an admin credential on the server.
Create scoped tokens
Each line of the tokens file is a token followed by up to three optional fields, in any order:
- Org: limits the token to addresses under
priompt://<org>/. A token without an org is an admin token for every org. - Expiry: a date such as
2026-12-31or an RFC 3339 timestamp. After it, the token fails withUnauthenticated: token expired. rw: allows writes: publish, branch, merge, and roll back. Without it, the token can only read.
To add scoped tokens:
-
Mint a token for each client:
priompt gen-tokenOutputc2e6bc7b33efea35fb7c1e113f9d5eb68957361bb3524f32b045bc7c6d26e959 -
Add one line per token to
tokens.txt:tokens.txt# token [org] [expiry] [rw]<admin-token> rw<acme-reader-token> acme<acme-author-token> acme rw<acme-contractor-token> acme 2026-12-31 rwReplace each
<...-token>with a value frompriompt gen-token. From v0.7.3,#starts a comment anywhere after the token. In v0.7.2 and earlier, the server reads a word after the token as a field, so<token> # read-onlyscopes the token to an org namedread-only; on those versions, put comments on their own lines. -
Restart the server so it reads the file again.
This is what each kind of token gets, from a test against a server with this file:
| Token | Request | Result |
|---|---|---|
acme reader | priompt list -prefix priompt://acme/ | Lists the acme prompts. |
acme reader | priompt list | PermissionDenied: token not authorized for prefix "" |
acme reader | priompt publish -uri priompt://acme/... | PermissionDenied: token is read-only |
acme author | priompt publish -uri priompt://globex/... | PermissionDenied: token not authorized for org "globex" |
| Expired | Any | Unauthenticated: token expired |
| Unknown | Any | Unauthenticated: invalid or missing token |
For SSO users and service accounts with short-lived tokens, run priompt-auth alongside the tokens file. See SSO and service accounts.
Rotate a token
To replace a token without downtime:
- Mint the new token with
priompt gen-tokenand add it totokens.txt. - Give the old token's line an expiry a few days out, such as
<old-token> acme 2026-11-01 rw. - Restart the server, and move each client to the new token.
- After the expiry, delete the old line and restart again.
Turn on TLS and mTLS
With TLS, the server encrypts every gRPC connection. With mTLS, it also refuses any client that doesn't present a certificate signed by your CA, before it checks tokens.
To create test certificates with OpenSSL:
-
Create a CA:
openssl req -x509 -newkey rsa:2048 -nodes -days 365 -keyout ca.key -out ca.pem -subj "/CN=priompt-ca" -
Create the server certificate. List every name clients use in
subjectAltName:openssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr -subj "/CN=localhost"printf "subjectAltName=DNS:localhost,IP:127.0.0.1\n" > server.extopenssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -days 365 -out server.pem -extfile server.extOutputCertificate request self-signature oksubject=CN=localhost -
For mTLS, create a client certificate signed by the same CA:
openssl req -newkey rsa:2048 -nodes -keyout client.key -out client.csr -subj "/CN=agent"openssl x509 -req -in client.csr -CA ca.pem -CAkey ca.key -CAcreateserial -days 365 -out client.pemOutputCertificate request self-signature oksubject=CN=agent
For production, use certificates from your own CA with the server's real hostname, such as prompts.example.com.
To serve over TLS:
-
Start the server with the certificate and key. Add
-client-cato require client certificates:priompt serve -tokens-file tokens.txt -tls-cert server.pem -tls-key server.key -client-ca ca.pemOutput2026/10/18 06:10:50 priompt v0.7.4 (unknown) serving on :8443 (tls=true mtls=true auth=1-token cache=mem embed=lexical(offline) nats=127.0.0.1:4222 metrics=:2112 ratelimit=0/s)Pass both
-tls-certand-tls-key. With only-tls-cert, the server serves plaintext, even though its startup line saystls=true.-client-cawithout them stops the server with-client-ca requires -tls-cert and -tls-key. -
Connect with the CA certificate, and with the client certificate for mTLS:
priompt list -tls -ca-cert ca.pem -cert client.pem -key client.keyOutputpriompt://acme/onboarding/welcome 22e5cc10ac0f
A plaintext client gets rpc error: code = Unavailable desc = connection error: desc = "error reading server preface: EOF". Under mTLS, a client without a certificate also gets Unavailable. The SDKs take the same three files: see Python SDK and JavaScript SDK.
Require a token on the broker
Change events go out on the server's embedded NATS broker, which listens on 127.0.0.1:4222 by default. An event names the prompt and carries the verdict that agents use to decide whether to reload, so an open broker leaks your prompt names and lets anyone forge events.
To expose the broker to other hosts:
-
Set a broker token and bind the broker to a reachable address:
export PRIOMPT_NATS_TOKEN=$(priompt gen-token)priompt serve -tokens-file tokens.txt -nats-addr 0.0.0.0:4222Without a token, the server refuses a non-loopback address:
Output-nats-addr 0.0.0.0:4222 is not loopback: set -nats-token (or PRIOMPT_NATS_TOKEN) so subscribers must authenticate, or bind NATS to 127.0.0.1 -
Give each subscriber its own API token inside the broker URL, the same token it uses for gRPC:
priompt watch -uri priompt://acme/onboarding/welcome -nats-url nats://<api-token>@prompts.example.com:4222Output2026/10/19 00:55:22 watching priompt://acme/onboarding/welcome on nats://***@prompts.example.com:4222priompt watchhides the credential when it logs the URL.
The broker accepts two kinds of credential:
- An API token: a tokens-file token,
PRIOMPT_TOKEN, or apriompt-authJWT. It can only subscribe, never publish. A token scoped to an org can subscribe only to that org's prompts, and an admin token to every org. The server accepts API tokens on the broker when both-nats-tokenand API authentication are set. - The broker token (
-nats-token): the server's own credential, with the right to subscribe to every org and to publish events. Keep it on the server and give it to no agent.
A wrong token fails with nats: Authorization Violation. A subscription outside the token's org fails with Permissions Violation for Subscription to "priompt.<org>....". Requires v0.7.3 or later; earlier versions accept only the broker token, which gives every holder full rights.
From v0.7.4, the broker closes a subscriber's connection when its API token or JWT expires, with the error User Authentication Expired; the client must reconnect with a valid token. Removing a token from the tokens file takes effect when the server restarts, which also closes open connections. In v0.7.3, an open subscription stays open after its token expires.
Encrypt data at rest
Set PRIOMPT_ENCRYPTION_KEY to encrypt prompt content in the database and in the Redis cache. See Storage and backups for the procedure and key handling.
Next steps
- Production checklist: every setting to check before a server takes real traffic.
- Orgs, tokens, and access: how the server authorizes each request.
- SSO and service accounts: short-lived tokens from your identity provider.
- Monitoring and rate limits: see denied requests in metrics and the audit log.