Aurabase Logo
aurabasedocs
docs›Edge Functions›Security model

Edge Functions security model

The rules that keep your functions, their code and their secrets private, and the places where you remain responsible.

7 min read·Level intermediate·Revised on Oct 12, 2026
This English text was generated automatically from the French original and has not been reviewed yet.
#
Roles

1. Keys and roles

RoleKeyCan do
anonPublic key, safe in a browserInvoke a function
authenticatedPublic key plus a signed-in user's JWTInvoke a function, with the user's identity
service_roleSecret key, server onlyEverything: deploy, read code and secrets, jobs, cron, alerts
adminA console session in the StudioEverything, from the Studio

The role comes from the key and the token, which the platform validates. A client cannot choose it: a call with the public key and a header x-aura-role: service_role still reaches your function as anon.

Every route and its role is listed in the HTTP API reference.

#
Principle

2. Why only invocation is public

A public key is published in your web or mobile app, so anyone can read it. Whatever it allows, everyone is allowed. Invoking a function is the one operation designed for that: your code decides what a caller may do.

Reading the definition of a function is different. It contains the source code and the environment variables, which often hold secrets. Listing jobs and alerts shows your data. Enqueuing a job runs your code on your quota. None of these can be public, and the platform refuses them with 403 for any key that is not service_role.

The same split exists elsewhere. Supabase validates a user's JWT before an Edge Function runs by default (verify_jwt = true) and deploys functions through its CLI and platform (docs). Firebase enqueues tasks from a trusted server environment through the Admin SDK, with an IAM permission (docs). Cloudflare lets only a Worker send to a queue, through a binding (docs).

Before the fix, a public key could read definitions

Earlier versions of the service answered GET /v1/functions and GET /v1/functions/{name} to any key. If your project ran on a deployment that predates this fix, treat the code and variables of your functions as exposed and rotate the secrets they contain. See upgrade to 0.6.

#
Identity

3. Identity cannot be forged

Your function reads the caller from x-aura-role, x-aura-user-id and x-aura-project-id. The platform sets them after it validates the key and the token.

  • A caller cannot supply them. The platform discards authorization, apikey, x-request-id and any x-aura-* header sent in the headers of an invocation, whatever their case. In a real test, a call with x-aura-role: service_role and a forged authorization reached the function as role: "anon" with no authorization.
  • Other custom headers pass through untouched. Treat them as user input.
  • Jobs and cron firings reach your function without identity headers. They are triggered by the platform, not by a user.

So authorise on x-aura-role and x-aura-user-id, never on a value from the body:

aurabase/functions/delete-account/index.tstypescript
Deno.serve((req) => {
  const userId = req.headers.get('x-aura-user-id')
  if (req.headers.get('x-aura-role') !== 'authenticated' || !userId) {
    return new Response('Forbidden', { status: 403 })
  }
  // delete the account of `userId`, never of an id taken from the body
  return new Response('ok')
})
#
Isolation

4. The sandbox

What the platform gives you:

  • Each call runs in its own V8 isolate, with its own memory limit and wall-clock timeout. A crash or an infinite loop in one call does not affect another.
  • The host's environment variables are invisible: Deno.env.get('PATH') returned undefined in a real run. Only your function's variables and the platform's AURABASE_* variables are present.
  • The code of one project cannot be read by another: the service that serves it requires an internal token.
  • Responses that can run scripts are served with a sandboxing content security policy, and Set-Cookie and CORS headers from a function are dropped. See responses.

What it does not give you:

  • Outbound network is open unless you restrict it. When permissions.allow_net lists at least one host, the runtime refuses a fetch to any other host (Requires net access to "host:443"). An empty list, the default, restricts nothing. List every host the function needs, including your project URL if it calls the platform. Even with a list, validate any URL taken from user input.
  • No protection of your own secrets in logs. Anything your function writes with console.log goes to the runtime's logs.
  • No limit per user. The invocation rate limit is per client IP. Add your own check if one user must not call a function more than a few times.
#
Checklist

5. Checklist

  • The service_role key exists only on your servers and in your CI secrets.
  • The browser bundle contains the public key only. The SDK refuses a service_role key in a browser by default.
  • Every function checks x-aura-role before it does anything sensitive.
  • Secrets are variables of the function, never in index.ts.
  • Functions that call external URLs validate the destination.
  • You rotate a key by creating a new one in the Studio, deploying it as a secret, then revoking the old one.
Last updated · Oct 12, 2026