<!-- Source: https://docs.snoutdata.com/cloud/functions -->

# Snout Functions

Your own TypeScript, answering at
`https://<ref>.api.snoutdata.com/functions/v1/<name>`. The runtime is Deno, so there is no build
step, no bundler and no `node_modules`: you write TypeScript and deploy the folder.

This is the one part of [the project API](https://docs.snoutdata.com/cloud/api) that is fully self-serve today, on every plan
including free.

## Deploy one

```bash
snoutdata functions deploy hello
snoutdata functions list
snoutdata functions delete hello
```

`deploy` reads `supabase/functions/<name>/` and falls back to `functions/<name>/`, so a project
moving over from Supabase needs no rearranging. `--dir` overrides both, and `--entrypoint` names
the file to start if it is not `index.ts`.

```ts
// supabase/functions/hello/index.ts
Deno.serve(async (req) => {
  const { name } = await req.json()
  return Response.json({ hello: name })
})
```

```bash
$ snoutdata functions deploy hello
$ curl -H "apikey: $ANON_KEY" -H 'content-type: application/json' \
    -d '{"name":"world"}' \
    https://<ref>.api.snoutdata.com/functions/v1/hello
{"hello":"world"}
```

A cold start is about **52 ms** and a warm call about **2.9 ms**, measured on the live host.

## Who may call it

**By default, a caller needs one of your project's API keys.** That is the right default: a
function is arbitrary code with a network attached, and the cost of getting this wrong is a
stranger running it.

`--no-verify-jwt` removes that check, which makes the URL callable by anybody who knows it. It is
exactly what a webhook receiver needs, because Stripe and GitHub cannot send your API key, and it
is a mistake anywhere else. A function deployed that way must check the sender's signature itself.
The CLI prints the consequence back to you after every deploy that uses it, in words, because a
flag whose effect is invisible in the output is a flag somebody leaves on.

## Secrets

Functions read their configuration from environment variables, and every function in a project
gets every secret, which is what somebody moving a project over expects.

```bash
snoutdata secrets set STRIPE_KEY=sk_live_...      # the form everybody expects
snoutdata secrets set STRIPE_KEY --stdin          # the value from a pipe
snoutdata secrets list
snoutdata secrets unset STRIPE_KEY
```

**Nothing ever prints a value back.** `list` shows names, sizes and when each was last set, which
answers the real question ("is the thing I set the thing that is there") without the control plane
ever growing an endpoint that returns a secret. There is no `get`, deliberately, and no MCP tool
that sets one: a secret in a tool call is a secret in an audit log.

`NAME=value` on a command line puts the value in your shell history and in `ps`. That form is not
refused, because a refusal people work around with `echo` is worse than a sentence they read, but
`--stdin` is what a CI job should use.

## Limits

| | Free | Plus | Pro |
| --- | --- | --- | --- |
| Functions per project | 5 | 25 | 100 |
| Memory per invocation | 128 MB | 256 MB | 512 MB |
| Wall clock per invocation | 10 s | 30 s | 55 s |
| CPU per invocation | 2 s | 8 s | 20 s |

A bundle may be **2 MB of text across at most 200 files**, on every plan. That is an enormous
function, and the cap is really about the courier rather than your code: a bundle travels through
a JSON body and a database column on its way to object storage, and this is the number that keeps
that path boring.

The wall clock cannot go past 59 seconds whatever the plan, because the front door gives up at 60.
The limits are set a second under it deliberately, so a function that runs too long is refused by
the runtime with a sentence you can act on, rather than by the proxy with a bad gateway.

## What a function can reach

The internet, and your own database with the keys you give it.

**It cannot reach the machine it runs on.** The runtime has its own network with the cloud
provider's internal addresses sunk, which was measured from inside a real function on a real host:
the instance metadata service refuses, and the public internet answers normally. That is the
property the whole design rests on, since the code running there is yours and one day somebody
else's.

## For an agent

Four MCP tools cover this: `deploy_function`, `list_functions`, `delete_function` and
`list_function_secrets`. There is deliberately no tool that sets a secret. See
[for an agent](https://docs.snoutdata.com/cloud/agent#mcp-server).

## Also read

- [The project API](https://docs.snoutdata.com/cloud/api), for the other five paths and your two keys.
- [CLI reference](https://docs.snoutdata.com/cloud/cli#functions), for every flag.
