Cuttlefish SDK Reference - v0.1.0
    Preparing search index...

    Module @cuttlefish-sync/query-token

    @cuttlefish-sync/query-token

    Framework-agnostic server package for signing Cuttlefish queries with EdDSA (Ed25519) JWTs. Use it anywhere you can run Node.js to authorize live SQL subscriptions against Cuttlefish.

    npm install @cuttlefish-sync/query-token
    
    import { signQuery } from '@cuttlefish-sync/query-token'

    const { token, expires_in } = await signQuery({
    sql: 'SELECT * FROM users WHERE id = $1',
    params: [123],
    user_id: 'user_456',
    })

    // Use token to connect your client WebSocket before expires_in seconds pass

    Set these values in your server environment:

    CUTTLEFISH_CLIENT_ID=your_client_id
    CUTTLEFISH_PRIVATE_KEY='{"kty":"OKP","crv":"Ed25519",...}'
    CUTTLEFISH_UPSTREAM_ID=your_upstream_id
    CUTTLEFISH_EXPIRY_SECONDS=60 # optional override
    import { generateKeyPair, exportJWK } from 'jose'

    const { privateKey, publicKey } = await generateKeyPair('EdDSA', { crv: 'Ed25519' })
    const privateJWK = await exportJWK(privateKey)
    const publicJWK = await exportJWK(publicKey)

    console.log('Private Key (keep secret):', JSON.stringify(privateJWK))
    console.log('Public Key (upload to Cuttlefish):', JSON.stringify(publicJWK))
    1. Sign in to the Cuttlefish dashboard.
    2. Create a new client to receive a client_id.
    3. Upload the generated public key.
    4. Store the private key JSON string in your server environment.
    await signQuery({
    sql: 'SELECT 1',
    params: [],
    client_id: 'override_client',
    private_key: '{"kty":"OKP","crv":"Ed25519",...}',
    upstream_id: 'custom_upstream',
    expiry_seconds: 120,
    })

    Signs a SQL query and returns an EdDSA JWT token.

    Required parameters

    • sql: SQL query string using positional parameters ($1, $2, ...)
    • params: Array of parameter values

    Optional parameters

    • user_id: Defaults to 'anonymous'
    • client_id, private_key, upstream_id, expiry_seconds: Override environment defaults

    Return value

    {
    token: string
    expires_in: number
    }

    Errors

    • Missing configuration (e.g. CUTTLEFISH_CLIENT_ID is required)
    • Invalid JWK JSON or curve
    • Non-positive expiry_seconds
    import { signQuery } from '@cuttlefish-sync/server'

    async function main() {
    const result = await signQuery({
    sql: 'SELECT * FROM todos WHERE user_id = $1',
    params: ['user_123'],
    user_id: 'user_123',
    })

    console.log('Token:', result.token)
    console.log('Expires in:', result.expires_in, 'seconds')
    }

    main().catch((error) => {
    console.error('Failed to sign query:', error)
    process.exitCode = 1
    })
    'use server'
    import { signQuery } from '@cuttlefish-sync/query-token'
    import { auth } from '@/lib/auth'

    export async function signTodosQuery() {
    const session = await auth()
    if (!session?.user) {
    throw new Error('Unauthorized')
    }

    return await signQuery({
    sql: `
    SELECT id, title, completed, created_at
    FROM todos
    WHERE user_id = $1
    ORDER BY created_at DESC
    `,
    params: [session.user.id],
    user_id: session.user.id,
    expiry_seconds: 60,
    })
    }
    import express from 'express'
    import { signQuery } from '@cuttlefish-sync/query-token'

    const app = express()

    app.post('/api/sign-query', async (req, res) => {
    const user = req.user
    if (!user) {
    res.status(401).json({ error: 'Unauthorized' })
    return
    }

    const result = await signQuery({
    sql: 'SELECT * FROM messages WHERE org_id = $1',
    params: [user.orgId],
    user_id: user.id,
    })

    res.json(result)
    })
    // Short-lived token (30s)
    await signQuery({
    sql: 'SELECT * FROM sensitive_data',
    params: [],
    expiry_seconds: 30,
    })

    // Longer window (5m)
    await signQuery({
    sql: 'SELECT * FROM public_posts',
    params: [],
    expiry_seconds: 300,
    })
    await signQuery({
    sql: 'SELECT * FROM public_posts LIMIT 10',
    params: [],
    // user_id defaults to 'anonymous'
    })
    • iss: client_id
    • aud: upstream_id
    • sub: user identifier (or 'anonymous')
    • exp: expiry (seconds)
    • nbf: not-before (seconds)
    • iat: issued-at (seconds)
    • jti: random UUID per token
    • query: { sql, params }
    • EdDSA (Ed25519) signatures using your private JWK
    • Tamper detection via signature verification
    • Replay resistance with short expiries and unique jti
    • User scoping with sub
    1. Never ship private keys to the client.
    2. Rotate keys periodically through the dashboard.
    3. Keep expiry windows short unless strictly necessary.
    4. Include user identifiers in your SQL WHERE clauses.
    5. Fail closed—handle signing errors explicitly.
    import type {
    Config,
    SignQueryParams,
    SignQueryResult,
    } from '@cuttlefish-sync/query-token'

    FSL-1.1-Apache-2.0

    Type Aliases

    Config
    SignQueryParams
    SignQueryResult

    Functions

    signQuery