Skip to main content

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.

Open by default

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 priompt binary: 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:

  1. Create the tokens file:

    priompt init
    Output
    wrote admin token to tokens.txt

    start server: priompt serve -tokens-file tokens.txt
    point client: export PRIOMPT_URL=priompt://<token>@localhost:8443

    init refuses to overwrite an existing file: tokens.txt already exists; pass -force to overwrite.

  2. Start the server with the file:

    priompt serve -tokens-file tokens.txt

    The startup line reports how many tokens it loaded, for example auth=1-token.

  3. Point clients at the server with the printed URL:

    export PRIOMPT_URL=priompt://<token>@localhost:8443

    The priompt client commands and both SDKs read PRIOMPT_URL. The CLI also accepts the token alone in PRIOMPT_TOKEN.

A request without a valid token now fails with Unauthenticated: invalid or missing token.

PRIOMPT_TOKEN means two things

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-31 or an RFC 3339 timestamp. After it, the token fails with Unauthenticated: token expired.
  • rw: allows writes: publish, branch, merge, and roll back. Without it, the token can only read.

To add scoped tokens:

  1. Mint a token for each client:

    priompt gen-token
    Output
    c2e6bc7b33efea35fb7c1e113f9d5eb68957361bb3524f32b045bc7c6d26e959
  2. 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 rw

    Replace each <...-token> with a value from priompt 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-only scopes the token to an org named read-only; on those versions, put comments on their own lines.

  3. 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:

TokenRequestResult
acme readerpriompt list -prefix priompt://acme/Lists the acme prompts.
acme readerpriompt listPermissionDenied: token not authorized for prefix ""
acme readerpriompt publish -uri priompt://acme/...PermissionDenied: token is read-only
acme authorpriompt publish -uri priompt://globex/...PermissionDenied: token not authorized for org "globex"
ExpiredAnyUnauthenticated: token expired
UnknownAnyUnauthenticated: 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:

  1. Mint the new token with priompt gen-token and add it to tokens.txt.
  2. Give the old token's line an expiry a few days out, such as <old-token> acme 2026-11-01 rw.
  3. Restart the server, and move each client to the new token.
  4. 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:

  1. Create a CA:

    openssl req -x509 -newkey rsa:2048 -nodes -days 365 -keyout ca.key -out ca.pem -subj "/CN=priompt-ca"
  2. 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.ext
    openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -days 365 -out server.pem -extfile server.ext
    Output
    Certificate request self-signature ok
    subject=CN=localhost
  3. 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.pem
    Output
    Certificate request self-signature ok
    subject=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:

  1. Start the server with the certificate and key. Add -client-ca to require client certificates:

    priompt serve -tokens-file tokens.txt -tls-cert server.pem -tls-key server.key -client-ca ca.pem
    Output
    2026/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-cert and -tls-key. With only -tls-cert, the server serves plaintext, even though its startup line says tls=true. -client-ca without them stops the server with -client-ca requires -tls-cert and -tls-key.

  2. Connect with the CA certificate, and with the client certificate for mTLS:

    priompt list -tls -ca-cert ca.pem -cert client.pem -key client.key
    Output
    priompt://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:

  1. 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:4222

    Without 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
  2. 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:4222
    Output
    2026/10/19 00:55:22 watching priompt://acme/onboarding/welcome on nats://***@prompts.example.com:4222

    priompt watch hides the credential when it logs the URL.

The broker accepts two kinds of credential:

  • An API token: a tokens-file token, PRIOMPT_TOKEN, or a priompt-auth JWT. 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-token and 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.

note

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​