The external server serves the API and static UI on port 8080 by default.
The internal server serves /healthz, /readyz, and /metrics on port 8888 by default.
The following config can be set via environment variables.
| Variable | Required | Default |
|---|---|---|
| SERVER_SALT | ||
| DATABASE_TYPE | in-memory | |
| REDIS_SERVER | localhost | |
| REDIS_PORT | 6379 | |
| SERVER_PORT | 8080 | |
| HEALTH_PORT | 8888 | |
| LOG_LEVEL | info | |
| VALID_FOR_OPTIONS | 3600,7200,43200,86400 | |
| MAX_SECRET_BYTES | 10485760 | |
| MAX_FILES | 20 | |
| MAX_FILE_SIZE_BYTES | 104857600 | |
| ENABLE_HSTS | false | |
| HSTS_MAX_AGE_SECONDS | 31536000 | |
| GRACEFUL_SHUTDOWN_SECONDS | 25 | |
| READINESS_DRAIN_SECONDS | 5 | |
| SHUTDOWN_HARD_SECONDS | 3 |
Used as the PBKDF2 salt for secret encryption and decryption.
The current implementation does not require this value and does not validate its length or entropy. For production deployments, set it to a stable, high-entropy value and protect it as secret material. Changing it prevents existing stored secrets from being decrypted.
Can either be in-memory or redis.
Address to your redis server.
Used to specify the port your redis server is using.
Listen port for api and ui endpoint.
Listen port for health endpoint, used mainly for liveness probes.
Used to specify log levels. Valid values are debug, info, warn, and error. Unknown values fall back to info.
Which options are available in the UI for secret expiration.
Only these values are accepted server-side for expires_in.
Maximum size of secret text content.
Maximum number of attached files.
Maximum size per file attachment.
Enable HSTS response header. Keep disabled unless TLS is correctly terminated upstream.
HSTS max-age value when ENABLE_HSTS=true.
Total shutdown budget after the process receives SIGTERM or SIGINT. This includes readiness draining, HTTP shutdown, and hard-cancel delay. Keep this lower than the platform termination grace period so cleanup can complete before the process is killed.
The HTTP shutdown timeout is calculated as GRACEFUL_SHUTDOWN_SECONDS - READINESS_DRAIN_SECONDS - SHUTDOWN_HARD_SECONDS.
Time to wait after /readyz starts returning 503 and before the public listener is shut down. This gives load balancers and orchestrators time to stop routing new requests to the instance.
Time to wait after the graceful shutdown timeout is reached so request contexts can observe cancellation before datastore resources are closed.
Startup fails if the config is invalid. The implementation validates that:
SERVER_PORTandHEALTH_PORTare different.MAX_SECRET_BYTES,MAX_FILES, andMAX_FILE_SIZE_BYTESare greater than0.VALID_FOR_OPTIONSis not empty and contains only unique positive values.HSTS_MAX_AGE_SECONDSis greater than0whenENABLE_HSTS=true.GRACEFUL_SHUTDOWN_SECONDS,READINESS_DRAIN_SECONDS, andSHUTDOWN_HARD_SECONDSare greater than0.GRACEFUL_SHUTDOWN_SECONDSis greater thanREADINESS_DRAIN_SECONDS + SHUTDOWN_HARD_SECONDS.
Content-only secrets can be created with JSON.
curl --request POST \
--url http://localhost:8080/api \
--header 'Content-Type: application/json' \
--data '{
"content": "some super secret stuff goes here",
"expires_in": 3600
}'Secrets with file attachments are created with multipart/form-data. The data field contains the same JSON payload as above, and each attached file is sent as a files field.
curl --request POST \
--url http://localhost:8080/api \
--form 'data={"content":"some super secret stuff goes here","expires_in":3600}' \
--form 'files=@./example.txt'Request fields:
content: secret text content.expires_in: number of seconds until the secret expires. The value must be one ofVALID_FOR_OPTIONS.files: optional map of file names to base64-encoded bytes in JSON requests, or one or more multipartfilesfields in multipart requests.
Either content or at least one file is required. content must be no larger than MAX_SECRET_BYTES. File count is limited by MAX_FILES, and each file is limited by MAX_FILE_SIZE_BYTES. Multipart requests also have an aggregate parsing limit of MAX_FILES * MAX_FILE_SIZE_BYTES + MAX_SECRET_BYTES + 1MiB.
The response status is 201 Created. The response body is a token that can be used to fetch the secret.
To fetch a secret, send a GET request to http://localhost:8080/api/<token>.
For example:
curl --request GET \
--url http://localhost:8080/api/Jsm9nDvKVhtAQEfz1Bukx7jHeKIBpPV8kX0B_a4w2rE.qAke0MYJ_uvGc30s6o85TiIn-qeBm_9S55ajlDzysRwExample response:
{
"content": "some super secret stuff goes here",
"files": {
"example.txt": "c29tZSBmaWxlIGNvbnRlbnQ="
},
"expires": "2026-06-26T12:00:00Z"
}File values are base64-encoded in the JSON response.
If the token is invalid, missing, expired, already read, or cannot decrypt the secret, the API returns 410 Gone with secret not found.
When a secret is fetched before it expires, it is deleted after the read. Expired secrets are deleted when accessed, and the datastore also removes expired secrets in the background.
GET /healthzreturns200 OKwhen the internal server is running.GET /readyzreturns200 OKuntil shutdown starts, then returns503 Service Unavailableduring the readiness drain period.GET /metricsexposes Prometheus metrics on the internal server.
Registered counters include:
secrets_readexpired_secrets_readnonexistent_secrets_readsecrets_createdsecrets_created_with_errorssecrets_deleted
- Keep
/healthz,/readyz, and/metricson a non-public network. - Use
/healthzfor liveness probes and/readyzfor readiness probes. - Set the platform termination grace period higher than
GRACEFUL_SHUTDOWN_SECONDS. - Always run behind TLS (reverse proxy / ingress is supported).
- External routes set
X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Permissions-Policy, andContent-Security-Policyheaders. - API routes set
Cache-Control: no-store, max-age=0,Pragma: no-cache, andExpires: 0. - HSTS is only sent when
ENABLE_HSTS=trueand the request is TLS or hasX-Forwarded-Proto: https. - Rotate
SERVER_SALTas part of incident response, but expect existing stored secrets to become unreadable after rotation. - If a link is leaked, treat the secret as compromised and rotate underlying credentials.