Munk - JS/TS & WASM run service
Use it for edge functions, A/B testing, security features or anything else you can create
This project is in: Beta
use hilly to upload code to munk easier
Munk.serve(async (req) => {
const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const data = await res.json();
return new Response(JSON.stringify({ data }))
})Get image from docker pull ghcr.io/edum22/munk-runner:<VERSION>
env:MUNK_DB_PATH-> Set custom save path for db | Default =/var/lib/munk/env:MUNK_IPINFO_PATH-> Set custom path for GeoIP database (ipinfo_lite.mmdb) | Defaults =$MUNK_DB_PATH/ipinfo_lite.mmdbenv:MUNK_ENCRYPTION_KEY-> Set encryption key for dbenv:MUNK_AUTH_HEADER_VALUE-> Set the admin auth header valueenv:MUNK_LOG_TOKEN-> Set the auth token for viewing logs | Defaults =env:MUNK_AUTH_HEADER_VALUEenv:MUNK_USE_SESSION-> Set totrueto useMunkSessionfor persistent function execution | Defaults =falseenv:MUNK_SESSION_WORKERS-> Set the number of worker threads perMunkSession| Defaults =4env:MUNK_MAX_BODY_SIZE-> Set maximum request body size (e.g.10MB,2048,500KB) | Defaults =10MBenv:MUNK_TELEMETRY-> Set it tofalseto not send telemetry | Defaults =true
The runtime is trying to conform to the wintertc spec.
The navigator.userAgent value should be = Munk
The use of env:MUNK_USE_SESSION CAN increase the throughput of the function BUT it will cache the v8/deno_core runtime per function, So if you need every request to use a clean deno_core isolate DO NOT use env:MUNK_USE_SESSION
The code execution has default timeout of 15s and cpu limit of 50ms, this can be changed when creating the function
Note
Users without a license is limited to 5 functions.
Function permissions restrict what system capabilities (network access, environment variables) the function's JavaScript isolate can perform.
"permissions": {
"allow_net": ["api.example.com"],
"allow_env": ["API_KEY"]
}allow_net: Controls network outbound connections (e.g.fetch()).allow_env: Controls access to environment variables.
- Omitted (Default): Unrestricted access. When
permissions(orallow_net/allow_env) is omitted, the function has unrestricted access. - Empty Array (
[]): No access granted. All network outbound calls or environment variable reads throw a permission error. - Wildcard (
["*"]): Unrestricted access. Allows connecting to any host/domain or reading any environment variable. - Specific Values (Allowlist): Restricted access. Allows connecting or reading only the specified domains, IP addresses, or environment variable names (e.g.,
["api.github.com", "example.com"]).
Munk.env.get(key: string) -> string | undefined | null
Munk.env.toObject() -> { key: value } | undefined | null
Munk.waitUntil(promise: Promise<any>)
Munk.serve((req: Request) => Response | Promise<Response>)These are inserted into the request to the function.
The Ip is taken from header: X-Forwarded-For (If you are not using a proxy like caddy this header can be spoofed by client)
x-munk-geo-as-domain: 'google.com'
x-munk-geo-as-name: 'Google LLC'
x-munk-geo-asn: 'AS15169'
x-munk-geo-continent: 'North America'
x-munk-geo-continent-code: 'NA'
x-munk-geo-country: 'United States'
x-munk-geo-country-code: 'US'
x-munk-geo-ip: '8.8.8.8'
GET - /health
{
"status": "ok",
"version": "<VERSION>",
"uptime_secs": 120
}GET - /api/functions or /api/function
Headers - [
munk-function-id: main,
munk-auth: ${VALUE_SET_IN_SETUP}
]
{
"functions": [
{
"id": "{functionId}",
"name": "my-func",
"created_at": "{function_created_at}",
"limits": {
"walltime": "15s",
"cputime": "50ms"
},
"permissions": {
"allow_net": ["api.example.com"],
"allow_env": ["DATABASE_URL"]
}
}
]
}POST - /api/functions or /api/function
Headers - [
munk-function-id: main,
munk-auth: ${VALUE_SET_IN_SETUP}
]
Body
{
"name": "my-func", // (optional)
"code": "Munk.serve(async (req) => new Response(`Hello from munk ${Munk.env.get('test')}`))",
"envs": [
{ "test": "this works, soo cool" }
],
"limits": { // (optional)
"walltime": "10s", // Default: 15s
"cputime": "75ms" // Default: 50ms
},
"permissions": { // (optional)
"allow_net": ["api.example.com"],
"allow_env": ["API_KEY"]
}
}header munk-function-id with the id of the created function.
Add header munk-function-id with the id of the function, then you will be redirected to the function.
ex: 'munk-function-id': '604qi60u0h0v'
DELETE - /api/functions?id={munk-function-id} or /api/function?id={munk-function-id}
Headers - [
munk-function-id: main,
munk-auth: ${VALUE_SET_IN_SETUP}
]
Deleting a function also deletes all associated logs.
status code 204
GET - /api/logs/stream
Headers - [
munk-function-id: main,
Authorization: Bearer ${MUNK_LOG_TOKEN} (or Bearer ${MUNK_AUTH_HEADER_VALUE} if no log token is set)
]
Note
Log endpoints are rate-limited to 30 requests per minute per IP (returns 429 Too Many Requests when exceeded).
A real-time Server-Sent Events (SSE) stream of type text/event-stream. Each event data is a JSON string representing a function execution log:
{
"id": "{munk-function-id}",
"is_error": false,
"message": "log message from code execution"
}GET - /api/logs?id={function_id}&limit={limit}
Headers - [
munk-function-id: main,
munk-auth: ${VALUE_SET_IN_SETUP}
]
Query parameters:
id(required): function IDlimit(optional): max number of logs to fetch (default: 100, max: 1000)
Note
Log endpoints are rate-limited to 30 requests per minute per IP (returns 429 Too Many Requests when exceeded).
{
"logs": [
{
"id": "{log_id}",
"function_id": "{function_id}",
"is_error": false,
"message": "log message from code execution",
"created_at": "2026-07-19 14:00:00"
}
]
}You should probably use a reverse proxy like nginx or caddy and setup something like this:
https://{functionId}.domain.run
->
http://localhost:3000
- with header
'munk-function-id': '{functionId}'