nacl and jwt are synchronous crypto APIs for deployed code. Import them from @flower-js/sdk/crypto or the SDK root. They only work inside Flower, not in Node or a browser.
NaCl
- Bytes in and out are
Uint8Array. Inputs are never modified. - Key arguments also accept managed key handles.
- Wrong types or lengths throw. Failed authentication returns
null(orfalsefor detached verify). - Random functions need system entropy, which is only available in mutations. In queries, pass explicit seeds, keys and nonces.
| API | Contract |
|---|---|
nacl.randomBytes(length): Uint8Array | Random bytes. Mutations only (unless a custom PRNG is set). Length is capped by result budgets. |
nacl.setPRNG(source: NaClPRNG | null): void | Replace the random source for NaCl in this callback; null restores the default. You are responsible for its safety; a weak one can repeat keys or nonces. Doesn't affect JWT nonces. |
nacl.secretbox(message, nonce, key): Uint8Array | XSalsa20-Poly1305 encryption. 32-byte key, 24-byte nonce; output is a 16-byte tag plus ciphertext. Never reuse a nonce with the same key. |
nacl.secretbox.open(box, nonce, key): Uint8Array | null | Decrypt; null if tampered, wrong key, or too short. |
nacl.scalarMult(secret, publicKey): Uint8Array | X25519, 32-byte inputs and output. Returns all zeros for low-order peer points; your protocol must check for that. |
nacl.scalarMult.base(secret): Uint8Array | X25519 public key from a 32-byte secret. |
nacl.box(message, nonce, publicKey, secretKey): Uint8Array | Public-key encryption. 32-byte keys, 24-byte nonce, 16 bytes overhead. |
nacl.box.open(box, nonce, publicKey, secretKey): Uint8Array | null | Decrypt with the peer's public key and your secret key; null on failure. |
nacl.box.before(publicKey, secretKey): Uint8Array | SharedKey | Precompute a shared key: 32 bytes for raw keys, an opaque SharedKey for managed ones. Valid only in the current callback. |
nacl.box.after(message, nonce, sharedKey): Uint8Array | Same as secretbox with the shared key. |
nacl.box.open.after(box, nonce, sharedKey): Uint8Array | null | Same as secretbox.open. |
nacl.box.keyPair(): NaClKeyPair | Random X25519 key pair. Mutations only. |
nacl.box.keyPair.fromSecretKey(secret): NaClKeyPair | Key pair from a 32-byte secret. Works in queries. |
nacl.sign(message, secretKey): Uint8Array | Ed25519; returns 64-byte signature plus message. Secret key is 64 bytes (seed + public key). |
nacl.sign.open(signedMessage, publicKey): Uint8Array | null | Verify and return the message; null if invalid. 32-byte public key. |
nacl.sign.detached(message, secretKey): Uint8Array | 64-byte Ed25519 signature. |
nacl.sign.detached.verify(message, signature, publicKey): boolean | Verify a detached signature; false if invalid. Rejects weak keys and malleable signatures. |
nacl.sign.keyPair(): NaClKeyPair | Random Ed25519 key pair. Mutations only. |
nacl.sign.keyPair.fromSeed(seed): NaClKeyPair | Key pair from a 32-byte seed. Works in queries. |
nacl.sign.keyPair.fromSecretKey(secret): NaClKeyPair | Key pair from a 64-byte secret. Throws if its public half doesn't match the seed. |
nacl.hash(message): Uint8Array | SHA-512, 64 bytes. Not a password hash. |
nacl.verify(a, b): boolean | Constant-time equality. false for different lengths or two empty arrays. |
nacl.box.after.open(box, nonce, key): Uint8Array | null | Alias of secretbox.open. |
Every NaCl length constant
| API | Contract |
|---|---|
nacl.secretbox.keyLength | 32 bytes. |
nacl.secretbox.nonceLength | 24 bytes. |
nacl.secretbox.overheadLength | 16 bytes. |
nacl.scalarMult.scalarLength | 32 bytes. |
nacl.scalarMult.groupElementLength | 32 bytes. |
nacl.box.publicKeyLength | 32 bytes. |
nacl.box.secretKeyLength | 32 bytes. |
nacl.box.sharedKeyLength | 32 bytes. |
nacl.box.nonceLength | 24 bytes. |
nacl.box.overheadLength | 16 bytes. |
nacl.box.after.keyLength | 32 bytes. |
nacl.box.after.nonceLength | 24 bytes. |
nacl.box.after.overheadLength | 16 bytes. |
nacl.sign.publicKeyLength | 32 bytes. |
nacl.sign.secretKeyLength | 64 bytes. |
nacl.sign.seedLength | 32 bytes. |
nacl.sign.signatureLength | 64 bytes. |
nacl.hash.hashLength | 64 bytes. |
Formats and names match TweetNaCl, except Ed25519 is stricter (see sign.detached.verify and sign.keyPair.fromSecretKey). These are primitives: peer identity, key rotation and nonce management are up to you.
JWT
| API | Contract |
|---|---|
jwt.sign(claims, key, options): string | Sign a compact JWS with HS256 (32+ byte secret), RS256, ES256 or EdDSA. Deterministic; works in queries. Adds no exp or other claims for you. |
jwt.verify<Claims>(token, key, options): JWTVerified<Claims> | Check the signature against your key and allowed algorithms, then check standard claims. Returns claims and header; throws if invalid. |
jwt.encrypt(claims, key, options = {}): string | Compact JWE, dir + A256GCM only. 32-byte key or managed A256GCM handle. Omit the nonce only in a mutation; if you pass one, keep it unique. Anyone with the key can also create tokens. |
jwt.decrypt<Claims>(token, key, options = {}): JWTVerified<Claims> | Decrypt and check standard claims. Throws on any failure. |
| API | Contract |
|---|---|
NaClKeyPair | {publicKey: Uint8Array; secretKey: Uint8Array}, in separate buffers. |
NaClPRNG | (output: Uint8Array, length: number) => void. Fill exactly length bytes. If it throws, the output is zeroed and the error propagates. |
JWTAlgorithm | "HS256" | "RS256" | "ES256" | "EdDSA". ES256 is P-256; EdDSA is Ed25519. The token header never picks the algorithm. |
JWTKey | Uint8Array | string | ManagedKey. HMAC/AES keys are bytes; RSA/EC keys are PEM strings or DER bytes. NaCl raw keys are not valid here. |
JWTKeyFormat | "raw" | "pem" | "der". Defaults: pem for strings, raw for bytes; request der explicitly. PEM: PKCS#8, SPKI and PKCS#1. Certificates and SEC1 are not supported. |
JWTClaims | Readonly<Record<string, Json>>. exp, nbf and iat are in seconds, not milliseconds. |
JWTSignOptions | {algorithm; keyFormat?; kid?; typ?}. algorithm is required for raw keys; typ defaults to JWT. |
JWTValidationOptions | {issuer?; audience?: readonly string[]; subject?; clockToleranceSeconds?; requireExpiration?; typ?}. exp is required by default; tolerance defaults to 0. If the token has aud, you must pass audience. |
JWTVerifyOptions | JWTValidationOptions & {algorithms: readonly JWTAlgorithm[]; keyFormat?}. A required, nonempty allowlist; don't mix symmetric and asymmetric algorithms. |
JWTEncryptOptions | {nonce?: Uint8Array; kid?: string; typ?: string}. Nonce is 12 bytes and must be unique per key. With a managed key, Flower sets kid. |
JWTProtectedHeader | {alg: JWTAlgorithm | "dir"; enc?: "A256GCM"; kid?: string; typ?: string}. kid is informational; it doesn't select a key. |
JWTVerified | JWTVerified<Claims extends object = JWTClaims> = {claims: Claims; protectedHeader: JWTProtectedHeader}. The type parameter doesn't validate your custom claims. |
Claim checks:
expis required unlessrequireExpiration: false, and is always checked when present.nbfis checked when present.iatis type-checked only; there's no max age.issuer,subjectandtypmust match exactly. At least oneaudiencemust match.- Time is Flower's invocation clock. Queries that verify tokens re-run as time passes, so an expired token isn't accepted from cache.
- Only
alg,kid,typ(andenc) headers are allowed. No JWKS fetching and no unverified decode. Validate custom claims yourself.
Randomness and limits
- Built-in randomness works only in mutations: not in queries, derived values, or bundle initialization. Elsewhere, pass explicit seeds, keys and nonces.
- Method arguments, stored values and results are JSON. Encode bytes (for example base64) yourself.
- Raw keys stored in bundles or records are ordinary replicated data. Use managed keys to keep private bytes out of your code.
- Input and output sizes are each capped by
FLOWER_RESULT_MAX_BYTES; native memory byFLOWER_RUST_MEMORY_BYTES. Exceeding a budget fails the whole call, even if your code catches the error. - A long crypto call can't be interrupted midway; the deadline is checked before and after.
See the usage guide, native implementation, and binary guest ABI.