Hono
Server-side encryption middleware for Hono. Automatically decrypts incoming requests and encrypts outgoing responses — zero changes to your handler logic.
Quick Start
Install the package:
bun add @ciph/hono @ciph/core honoMount the middleware in your Hono app:
import { Hono } from "hono"
import { ciph } from "@ciph/hono"
const app = new Hono()
// Mount once at app root — all routes below are encrypted
app.use("*", ciph({
secret: process.env.CIPH_SECRET!,
}))
// Your handlers work unchanged
app.post("/api/users", async (c) => {
// Request body is auto-decrypted
const data = await c.req.json()
// Store user
const user = await db.users.create(data)
// Response body is auto-encrypted
return c.json(user)
})The shared secret must match your frontend CIPH_SECRET exactly. Store it in environment variables, never in code.
Configuration
Full Config Options
interface CiphConfig {
/** Shared secret (required). Same as CIPH_SECRET on frontend. */
secret: string
/** Routes to skip encryption. Default: ["/health", "/ciph", "/ciph/*"] */
excludeRoutes?: string[]
/** Validate IP address in fingerprint. Default: true */
strictFingerprint?: boolean
/** Max payload size in bytes. Default: 10 MB */
maxPayloadSize?: number
/** Allow unencrypted requests (migration mode). Default: false */
allowUnencrypted?: boolean
}Example: Development Setup
app.use("*", ciph({
secret: process.env.CIPH_SECRET!,
// Disable IP validation behind proxy
strictFingerprint: false,
// Increase payload limit for file uploads
maxPayloadSize: 50 * 1024 * 1024, // 50 MB
}))Excluding Routes
Skip encryption for specific routes using exact matches or glob patterns:
ciph({
secret: process.env.CIPH_SECRET!,
excludeRoutes: [
"/health", // Health checks
"/status", // Status endpoints
"/public/*", // Public resources
"/webhooks/*", // Third-party webhooks
]
})Excluded routes must not process sensitive data. They bypass encryption entirely.
Per-Route Exclusion
Exclude a single route from encryption:
import { ciphExclude } from "@ciph/hono"
app.post("/webhooks/stripe", ciphExclude(), async (c) => {
// This handler receives unencrypted body
const event = await c.req.json()
await processStripeWebhook(event)
return c.json({ ok: true })
})DevTools Inspector
Mount the backend inspector to debug encrypted traffic in real-time:
import { ciphDevServer } from "@ciph/devtools-server"
app.route("/ciph", ciphDevServer({
secret: process.env.CIPH_SECRET!,
}))Then open http://localhost:3000/ciph in your browser to inspect all encrypted requests/responses.
DevTools are automatically disabled in production regardless of configuration.
How It Works
Request Flow (Decryption)
- Read X-Fingerprint header from client
- Decrypt fingerprint using shared secret
- Validate fingerprint — check IP & user agent match
- Derive AES key from secret + fingerprint
- Decrypt body using key
- Inject plain body into
c.reqfor handler
Response Flow (Encryption)
- Intercept response before sending
- Serialize response body to JSON/Buffer
- Encrypt body using same derived key
- Send ciphertext with
Content-Type: text/plain