Machine-to-machine authentication for create endpoints
Backend services can now create secrets, files, and secret requests when --require-auth is enabled without going through the interactive OIDC login. A new --api-token flag (or API_TOKEN env var) accepts one or more name:secret pairs; requests authenticate via Authorization: Bearer <secret> and are matched in constant time. Token secrets are hashed once at startup and only the digest is kept in memory.
- Tokens gate creation only (
/create/secret,/create/file,/request); retrieving auth-protected secrets still requires a session - Service identities are attributed in audit logs as
service:<name>and bypass--oidc-allowed-domains, since there's no email to check - Startup validation rejects malformed tokens, secrets shorter than 16 characters, duplicate names, and any
--api-tokenset without--require-auth
Fix S3 file store on Cloudflare R2
File uploads to Cloudflare R2 failed outright: R2 rejects PutObject requests carrying x-amz-tagging with a 501, and the file store tagged every object with its expiry. The tag was redundant — expiry was already written to the Expires header, which R2 does support — so tagging is dropped and the cleanup goroutine now reads expiry via HeadObject instead. Behavior against AWS S3 is unchanged.
Skip server lookups for terminal requests and receipts
Secret requests and read receipts now resolve terminal states from local storage instead of polling the server:
- Expired or already-fulfilled requests are read from the local cache; the navbar badge counts cached-fulfilled requests directly and only polls the ones still pending
- The receipts list skips lookups for receipts already marked viewed or past expiry, and the result-page panel stops polling on expiry as well as on view
This cuts recurring 404/no-op requests for state that can no longer change.
Honor LOG_LEVEL and TRUSTED_PROXIES environment variables
Two configuration options documented in docs/server-options.md were silently ignored when set via environment variable:
LOG_LEVELwas only applied when passed as the--log-levelfits level through Viper so the flag, env var, and config file allworkTRUSTED_PROXIES(andOIDC_ALLOWED_DOMAINS) are list settings that Viper only comma-splits when passed as a flag — via env var the whole value came through as one entry, soTRUSTED_PROXIES=a,bnever matched. A comma-awansistently regardless of source
Secret requests: atomic lifecycle transitions in multi-instance deployments
Secret request state changes (fulfillment, key rotation) are now enforced atomically by the storage backend, using transactions in Redis and compare-and-swap in Memcached.
Previously, these transitions were only serialized within a single server process. In deployments running multiple yopass instances against a shared Redis or Memcached backend, concurrent operations on the same request could act on stale state:
- Two simultaneous fulfillments could both be accepted, with the second silently overwriting the first responder's encrypted secret.
- A fulfillment racing a revocation could re-store the deleted record, leaving a revoked request retrievable with its management token until it expired.
Single-instance deployments were not affected. Exploitation required possession of the request link (an authorized responder by design) and winning a very narrow timing window, so practical impact is limited, but revocation and single-fulfillment are guarantees this feature makes, and they now hold regardless of instance count.