Skip to content
This page View Markdown Open in ChatGPT Open in Claude

Sandbox settings

The code of an account runs in sandboxes : Docker containers of the account, one kind per language. This page says what each one is made of. Page : Operations → Sandbox Settings, in four tabs : Languages, Images and sandboxes, Import & Export Tools and Allowed Origins.

In short

  • Your code runs in sandboxes : Docker containers, one kind per language.
  • This page sets each language : its runtime, its version, its packages and its Dockerfile.
  • Add a package here before your code imports it.
Sandbox Settings : the languages, the settings of Python, and its packages
  1. The languages.
  2. Its runtime and its version.
  3. The packages your code may import.
Languages TypeScript / JavaScript, Python, PHP, C#, Go, Rust, Java, Kotlin : each with its own runtime, version, packages and Dockerfile
Images downloaded and built only for the languages your code is written in : in the background on every server when you save, or when its code first runs ; shared by every account with the same settings
Sandboxes the most sandboxes of one language at a time on a server : Auto by default (one sandbox, whose runners grow to the CPU cores of the server), for every server or for each one
Idle sandboxes kept by default (Never) ; removed after the minutes you set, for every server or for each one
Restart every sandboxes start anew after this many seconds, without dropping a request
Allowed origins the web origins allowed to call the APIs from a browser
Native languages the versions of each language installed on every server for the code marked runOnNativeProcess, and the one it runs on
Import & Export Tools the programs Import & Export and Automatic Backup run : Automatic, the version most compatible with each database, or the one you choose
Where code runs : sandboxes per account and language, and the native process API Maker one process per server, every core ; settings and schemas run here a run past its time is cut short, an error ends it sandboxes : Docker containers of each account account shop · TypeScript 1 sandbox, more while busy, up to the count account shop · Python its own runtime, packages and Dockerfile account crm · Go native process · runOnNativeProcess on the machine, beside API Maker : not isolated The trust model Containers run unprivileged, with no memory limit : a guard against faulty code more than hostile code. Separate clients : separate servers. Like the sandboxes, the native runner answers what the code asks of g.sys itself, and hands only the answer back.

A guard against faulty code, not hostile code

The containers run without privileges : Docker drops the rights of Linux down to a short list, a program can not gain new ones, and Docker's default seccomp filter applies. They have no memory limit, and the code of every account runs on the same machine. So they guard against faulty code more than against hostile code : run only code you trust, and use separate servers for clients who must not see each other's data. See the sandboxes in the security overview.

Sandboxes, Runtimes and Packages: Where Your Code Runs10 chapters and the transcript

The tabs

Tab What it holds Saved
Languages the list of the languages ; for the one chosen, its runtime and version, its native languages, its packages and its Dockerfile with Save
Images and sandboxes the images and sandboxes of every language on each server, then the sandboxes on the servers : how many, when an idle one goes, the restart with Save
Import & Export Tools the programs Import & Export and Automatic Backup run, with the version each one runs now on this server as soon as they change
Allowed Origins the web origins allowed to call the APIs from a browser as soon as they change
  • Save saves the two first tabs at once. A tab which holds changes not saved yet has a ✎ beside its name, and the bar says Unsaved changes : nothing is lost while another tab is shown.
  • Images and sandboxes has a red dot while a build failed on a server, an amber one while an image is being built. Allowed Origins says how many origins are allowed : with one, every other origin is refused.
  • The address names the tab shown (?am_sandbox_tab=images, tools or origins ; nothing for the languages) : a reload, or a link sent to someone, opens it. The documentation icon of the bar opens the part of this page about the tab shown.

Languages

The tab Languages. The list on the left has one row per language, with the image its sandboxes are built from and a dot for the state of that image : green ready, amber building, red failed, grey not built yet, or no code of the language yet. Select a language to edit it.

What no language owns is in the other tabs : the images and sandboxes of every language with the sandboxes on the servers, the tools of Import & Export and the allowed origins.

Runtime and version

  • TypeScript / JavaScript runs on Node.js or Bun. The code does not change with the runtime.
  • Each runtime has its own settings : click a runtime to edit its version and its Dockerfile. The packages are the ones of the language, shared by its runtimes.
  • Use by default : the runtime marked with a star runs every piece of code which names no runtime. Code can name its runtime itself, so one API runs on Node.js and another on Bun in the same account ; each runtime in use gets an image of its own.
  • Settings use no sandbox : the Basic Info of a custom API, the settings of APIs, schemas, the texts of internationalization and secrets are TypeScript which API Maker runs itself when you save them. They need no runtime, no image and no packages : an account which runs its code on Bun has no image of Node.js for them.
  • A runtime no code names and you never opened uses its default version : nothing has to be set for it.
  • The version is picked from a list : the released versions of the official image which API Maker runs, newest first (24 or 22.20.0 for Node.js, 3.13 for Python, 8.4 for PHP). The list is read from Docker Hub (C# : from the registry of Microsoft) and has a search field when it is long. Clear the choice for the default version.
  • API Maker runs the versions released after January 2023 : see the oldest version of each runtime. An older version is not in the list, and is refused when the settings are saved : Python 3.10 is too old : API Maker runs Python 3.12 and later. A version saved before, which is not offered any more, is shown as not offered until you pick another one.
  • PHP : only its released versions are offered (8.4, 8.4.12, no release candidate). The runner of PHP loads API Maker with the FFI extension, which the official images are built without : the image of the sandboxes gets it when it is built, and the build fails rather than give a sandbox without it.
  • Java and Kotlin : the version is the one of the JDK (25, 21). Kotlin runs on the JDK too, in an image of its own which gets the compiler of Kotlin when it is built.
  • The image the version gives is shown next to it. Without a version : node:24-slim, oven/bun:1-slim, python:3.13-slim, php:8.4-cli, mcr.microsoft.com/dotnet/sdk:10.0, golang:1.27, rust:1-slim, eclipse-temurin:25-jdk (Java and Kotlin).

Packages

Nothing is installed unless you add it. Search the registry of the language, pick a package and a version, and Add. The version is one of the list or one you type (a range as ^4.17.0) ; empty is the latest. A package can be switched off without removing it.

Language Registry Name Version
TypeScript npm lodash, @types/node 4.17.21, ^4.17.0, latest
Python PyPI requests, uvicorn[standard] 2.32.3, >=2,<3, ~=2.32
PHP Packagist guzzlehttp/guzzle 7.9.2, ^7.9
C# NuGet Newtonsoft.Json 13.0.3, 13.*, [1.0,2.0)
Go Go modules github.com/google/uuid v1.6.0, 1.6.0, latest
Rust crates.io serde[derive], tokio[rt-multi-thread,macros] 1, ^1.40
Java Maven Central com.google.code.gson:gson (group:artifact) 2.11.0, 33.4.0-jre, [1.0,2.0)
Kotlin Maven Central org.jetbrains.kotlinx:kotlinx-coroutines-core 1.10.2, [1.9,2.0)
  • PyPI and Go modules have no search : type the exact name, the versions come from the registry.
  • Maven Central is searched by words (gson), or by the whole name of a package (com.google.code.gson:gson). Java and Kotlin each have a list of their own.
  • A name or a version which can not be one is refused when you save, in words : 'requests; rm' is no name of a package of the Python Package Index….
  • While code runs nothing is downloaded : a package which is not in the list is not there.
  • Code on the native process (runOnNativeProcess) finds the packages too, in every language : the first time it runs, they are installed on the machine of the server, in a folder of their own, with the tool of the registry (npm, bun, pip, Composer, go, cargo, NuGet, Maven) and the versions the images get. Every account with the same packages shares that folder and its runner. The tool has to be on the machine (Composer for PHP ; Maven is fetched from Maven Central by the JDK). A package the registry does not have is said in the answer : the packages of Node.js could not be installed on this server : npm install : …. The code of JavaScript still finds the packages of the folder API Maker runs in.

Dockerfile

Every language has a default Dockerfile. Switch Customize on to write your own : it is one file with three markers API Maker fills in.

A Dockerfile of Python with a system library
1
2
3
4
5
6
7
8
FROM {{AM_BASE_IMAGE}}

# What your code needs from the system : built once, above the packages.
RUN apt-get update && apt-get install -y --no-install-recommends libpq5 && rm -rf /var/lib/apt/lists/*

{{AM_PACKAGES}}

{{AM_RUNNER}}
Marker Becomes
{{AM_BASE_IMAGE}} the image of the runtime in the version of the settings
{{AM_PACKAGES}} the tools of the registry, the folder the code runs in and your packages. PHP : the FFI extension first
{{AM_RUNNER}} the program of the sandbox, the runner of the language, and what starts them
  • Order matters for the time of a build : the lines above {{AM_PACKAGES}} are built once, a change of packages does not install the system again, and the runner comes last.
  • Without {{AM_PACKAGES}} the packages come right before the runner; without {{AM_RUNNER}} the runner comes at the end.
  • ENTRYPOINT and CMD are API Maker's; a marker in a comment is no marker; a marker written twice is refused.
  • A variable the code reads (process.env.TZ, os.environ['TZ'], getenv('TZ'), System.getenv("TZ")) is an ENV line : ENV TZ=UTC above {{AM_PACKAGES}}. It is part of the image : keep secrets in Secrets.
  • Kotlin : ENV AM_KOTLIN_VERSION=2.3.0 above {{AM_PACKAGES}} names the compiler the image gets.
  • PHP : a Dockerfile which starts from another image than {{AM_BASE_IMAGE}} needs the FFI extension, or the tools which install it (docker-php-ext-install, as the official images have) : without the extension the image is not built, and the log of the build says why.
  • Final Dockerfile shows the file as it is built, with the markers filled in and the steps API Maker adds for its own library. Reset to default brings the default back.

Native languages

Code marked Run on native process (runOnNativeProcess, on custom APIs, hooks, schedulers, events…) does not run in a sandbox : it runs on the servers themselves, so the servers need its language. The Native process part of the card of each language installs the versions you need, on every server, and says which one your native code runs on. The sandboxes are not concerned : their image has its runtime.

  • Install : pick a version and click Install. The list is read from the publisher of the language as it releases (nothing is written in API Maker) : the newest releases of each line, with their tags (LTS, current), their size and ★ for the newest release of the line the sandboxes run. Every server of the installation downloads the build of its own system and processor, checks it against the checksum its publisher gives, unpacks it, runs it once to see that it says the version which was downloaded, and only then shows it Ready. The progress of each server is shown while it installs.
  • Runs on : choose the version your native code of that language runs on, or The server's own program (the one the PATH of each server finds, as before : the page shows what each server has). A version is chosen once it is ready on the server the page asked ; until it is ready on every server, the native code which reaches a server where it is still installing waits for it within the time of the request.
  • Your versions are your own. Each admin installs and chooses for its own account : the native code of another admin keeps running on what that admin chose. Two admins who install the same version share its files on the disk. Remove takes a version off your list ; it leaves a server once no admin wants it any more and no runner runs on it. Nothing else on the server changes : not the system, not its PATH, not a version installed by hand or by API Maker Launchpad.
  • A failed install says why on the server where it failed (a publisher which could not be reached, a disk which is full, a checksum which does not match) ; Try again asks every server again. A server tries a failed install again by itself after 15 minutes, and a new server of the installation installs what the admins want when it starts.
  • Packages : the packages of the Sandbox Settings are installed with the version chosen (npm of that Node.js, pip of that Python, Composer of that PHP…), in a folder of their own for each version.
  • At most 8 versions of one language per account.
Install Node.js, Python, Go or Java on Every Server, per Admin10 chapters and the transcript
Language Builds from Checked with What to know
Node.js nodejs.org SHA-256 of SHASUMS256.txt npm comes with it
Bun github.com/oven-sh/bun SHA-256 GitHub gives the build for processors without AVX2 on such a server
Python python-build-standalone (the builds uv installs) SHA-256 GitHub gives pip comes with it
PHP static-php-cli (macOS, Linux), windows.php.net (Windows) SHA-256 of php.net (Windows) ; HTTPS only for the static builds Composer is installed with it, checked with its SHA-256. The static builds have no FFI : the g.sys calls of the code are answered by the server instead of in its runner, which is slower. PHP for Windows needs the Visual C++ runtime of Microsoft
.NET dotnet.microsoft.com SHA-512 of the release metadata the SDK, telemetry off
Go go.dev SHA-256 of go.dev needs a C compiler on the server (its plugins are built with cgo) ; not on Windows, where Go builds no plugins
Rust static.rust-lang.org SHA-256 of the manifest of the release the compiler, cargo and the standard library ; needs the C linker of the server (Linux, macOS) ; on Windows the toolchain of GNU, which brings its own
Java Eclipse Temurin (adoptium.net) SHA-256 of Adoptium the JDK
Kotlin github.com/JetBrains/kotlin SHA-256 GitHub gives, or the .sha256 of the release the compiler runs on a JDK : the Java version you chose for your native code, else the java of the server

Where the versions are

In __native_languages_installed, one folder per build (node/24.11.1-linux-x64), next to the program of API Maker : the folder of the installation when it keeps its versions side by side (next to releases/ and current, so every version of API Maker finds them after an update), the folder API Maker runs in for the package of npm, the folder of the program otherwise. AM_NATIVE_LANGUAGES_FOLDER in the environment of the process names another folder. The lists of releases are kept 6 hours (the ones of GitHub are asked with GITHUB_TOKEN when the server has one).

  • Windows : the runner of Go, Rust, .NET, Java and Kotlin is a script of sh : Git for Windows has to be installed on the server (the page says when a server has it). Node.js, Bun, Python and PHP need nothing else.
  • What a request is answered when the version chosen is not there : Node.js 24.11.1 is being installed on this server (42 %) : try again in a moment, or what its install said when it failed.
  • The choices are saved with the admin user (settings.nativeLanguages, in src/admin_user/data.yaml of the repository), with a history, and travel with the project through git : once a pull is over, every server installs the versions the branch names and lets go of the ones no admin wants any more. A branch committed before the versions were chosen names none.

Images and sandboxes

The tab Images and sandboxes : a table with a row for every language on each server, whichever language the tab Languages shows, and for each other runtime the code uses (Bun beside Node.js) ; the rows of the language chosen there are marked on their left edge. It shows the server the page asked at first. Below it, the card Sandboxes.

Column What it says
Language the language, and its runtime when it has more than one (★ : the one code which names none runs on)
Base image the image the sandboxes are built from, as the version gives it (node:24-slim)
Server the name of the server, this server for the one the page asked, not running now for a server which is away but built the image once
Image Ready, Building, Build failed (the error when the pointer is on it), Not built yet (a server builds it when the settings are saved, or when its code first runs there) or No code yet : no code of the account is written in this language, so nothing is downloaded for it (when an image is made)
Built when the build ended, or began while it runs ; the pointer says the date, and when another account built the same image (the image was on the server already)
Sandboxes the sandboxes of the language which run on that server : how many, their runners, the requests running, how long the oldest is up and the last used idle (the pointer says each sandbox with its port). None running, or — when the server did not say them within a few seconds
the log icon : the end of what the build printed
  • Filters, under the titles of the columns : a part of the name of a language or of an image (press Enter), a server (clear it for every server), the state of the image (Build failed lists the builds which failed, on every server when no server is chosen).
  • Saving builds the images which changed, of the languages your code is written in, on every server, in the background. The sandboxes of the account start anew with the new image; requests which were running finish on the old one.
  • ⟳ in the title of the language chosen starts its sandboxes anew on every server, with the same image. ⟳ in the title of Images and sandboxes asks the servers again.
  • An image is named after what it is made of : two accounts with the same runtime, version, packages and Dockerfile share one image. Images no code needs any more are removed every hour ; an image a sandbox still runs from stays.

When an image is made

An image is downloaded and built on a server only for a language the code of the account is written in. The settings of a language your code does not use make no image : saving every language on this page downloads nothing for the ones without code.

  • Code which needs an image : custom APIs, schedulers, listeners of events, hooks, web socket events, process initializers, migration scripts, test cases and the generators of auth providers, in use (not removed, not switched off), with a line of code, and not on the native process. The functions of the columns of a schema (validatorFun, conversionFun, defaultFun) need the image of JavaScript.
  • When you save this page, or click ⟳ on a language, every server makes the images of those languages ready in the background : their next requests do not wait.
  • When you save code of a compiled language (C#, Go, Rust, Java, Kotlin), code which names its runtime, or a custom API of a separate sandbox, its image is made ready at once, and compiled code is compiled.
  • Any other code has its image made the first time it runs on a server : that request waits for the download and the build (a minute or two for most languages, more for C#, Java and Kotlin), then the image stays.
  • Code on the native process (runOnNativeProcess) needs no image.

Sandboxes on the servers

The card Sandboxes, in the tab Images and sandboxes below the table, says how many sandboxes the account runs, how long an idle one stays, and when they start anew. It is the same for every language of the account : the settings of every server come first, then a row for each server.

Sandboxes of each language

The most sandboxes one language of the account runs at once on a server. Each sandbox opens one runner when it starts, and one more while all of its runners are busy, up to as many as the server has CPU cores : the runners of one sandbox keep every core busy. A sandbox does not belong to a thread : every thread of the API Maker process hands its code to the same sandboxes.

Auto (0, the default) is one sandbox, whose runners grow to the CPU cores of the server.

Cores of the server 2 4 10 32
Sandboxes of each language, with Auto 1 1 1 1
Runners of that sandbox at the most 2 4 10 32
With a count of 3 : runners at the most 3 × 2 3 × 4 3 × 10 3 × 32
  • − and + step from Auto to 50 ; type a number, or click Auto. Beside the field, the page says how many runners each sandbox opens on the server it asked.
  • A number above 1 adds sandboxes, each with its own runners : while one starts anew (a new image, a restart, a runner held by code which did not end in time), another takes the requests. Every sandbox of every language of every account is a container : it costs memory.
  • A separate sandbox of custom APIs keeps its own count (sandboxCount of the group).
  • The Images and sandboxes card says which sandboxes of each language run on each server, and the runners they have open.

Remove idle sandboxes after

A sandbox no request used for this many minutes is stopped, and gives its memory back. The next request starts one again, which takes a second or two.

  • Never (0, the default) : idle sandboxes stay, and the first request is always fast. They still go when their settings change, or when you click ⟳ on a language. An account which says nothing on a server which keeps its default shows Never chosen.
  • Empty : the default of each server, sandbox.removeSandboxInactiveSinceMinutes of its configuration (never, unless the Deployment settings or the .env file of the server say a number of minutes). The page says what it is on the server it asked.
  • Fractions are allowed, half a minute at the least, a year at the most. Every server looks at its sandboxes once a minute, so an idle sandbox goes within a minute after its time.
  • The runners of the server for native code (runOnNativeProcess, one for each runtime, shared by every account) end the same way, after the default of the server (never, unless it says minutes) : the next native code starts its runner again, and what an earlier run left in g.shared of that runner is gone.

On each server

Every server runs with the settings above, unless you switch on Own in its row : more sandboxes on a big server, fewer on a small one, idle sandboxes kept on the server which takes the traffic.

Column What it shows
Server its name, this server for the one the page asked, not running now for a server which is away, and its id with its version
Cores the CPU cores of the server : the most runners each of its sandboxes opens
Sandboxes what the server runs : its own count, the one of every server, or auto (one sandbox), with its runners (× 10 runners)
Remove idle after its own minutes, the ones of every server, or the default of the server
Own switched on, the row is edited in place : its count (Auto or a number) and its idle minutes (empty : the ones of every server ; Never : kept)
  • The rows are the servers which are running now, and the servers the account keeps settings for. A server is known by its id : uniqueServerId of its .env file, else the name of its machine and its port. Give your servers an id when their machines change names (containers).
  • The settings of a server which is not running now are kept, for the day it comes back : switch Own off to forget them.
  • Settings which name a server of another installation (a branch pulled from another project) are simply not used there.

What happens when you save

Nothing stops. How many sandboxes there are and how long an idle one stays are read at every request and every minute, so a change of them makes no sandbox anew :

  • more sandboxes : the next requests start them, one at a time, in the background ;
  • fewer sandboxes : the newest ones beyond the count take no more requests, and go once their requests are over ; the oldest stay, which x-am-run-in-sandbox pins requests to ;
  • other idle minutes : followed at the next look of the minute.

A change of what the sandboxes are made of (runtime, version, packages, Dockerfile, restart) starts them anew, as before.

Coming from API Maker 3

The count of an account of API Maker 3 (settings.sandbox.sandboxCountOverrideAdmin) is its count of every server until the page saves its own : the page shows it, and saving it writes sandboxCount, which wins over it. The old value stays as it was, for a server of version 3 on the same database. The count of the server (sandbox.sandboxCountForAdmin of the .env file) is read no more : every account is Auto (one sandbox, its runners up to the cores) unless it says otherwise, where API Maker 3 and the first betas of version 4 started 2 sandboxes. removeSandboxInactiveSinceMinutes: 0 keeps idle sandboxes, as it did in API Maker 3.

Time of a run

Every runner gives each piece of code it is handed the time of the item the code belongs to : the first runner of a sandbox, each runner a busy sandbox opens, and the runner of the server which runs the code marked runOnNativeProcess. Code which needs nothing of a sandbox runs in the server itself, and is given the same time. When the time is over, the request ends with the error API didn't respond in <ms>ms., and the runner which holds the code is set aside : it takes no more requests, and is ended once the other requests it runs are over, each in its own time. Only the code which did not end in time is cut short : a request which runs beside it on the same runner ends as it would have, or when its own time is over. The other runners of the sandbox go on, and the next code marked runOnNativeProcess meets a new runner of the server. The time a sandbox or a runner takes to start is not counted.

Code of Its time When it names none
a custom API, and the hooks of its versions customApiTimeoutInSeconds, in seconds sandboxReqTimeout of the configuration (13000 ms)
the hooks of the APIs of instances, databases, tables and system APIs the time of the request sandboxReqTimeout
an event listener eventListenerTimeoutInMinutes sandboxReqTimeout
a scheduler schedulerTimeoutInMinutes 10 minutes
a migration script dbMigrationTimeoutInMinutes 10 minutes
a process initializer, in each runner it runs in processInitializerTimeoutInMinutes sandboxReqTimeout
  • A request names a time of its own with the header x-am-sandbox-timeout : it wins over customApiTimeoutInSeconds.
  • The API testing page tries a scheduler, a migration script or a process initializer with the time it runs with.
  • A runner whose process initializer did not end in time stops waiting for it and takes requests.
  • A sandbox whose last runner was set aside takes no more requests : the next request starts a new one, and the old one goes once its requests are over.

Restart every

In the card Sandboxes, beside the count and the idle minutes. Sandboxes start anew after this many seconds, one after the other : a new one starts first and takes the requests, the old one finishes what it runs. Use it for code which holds memory it does not give back. Empty : never.

Separate Sandboxes for Heavy APIs: Bun, Sandbox Count and Restarts10 chapters and the transcript
  • The time counts from the start of each sandbox. A sandbox gives its place when a request comes for it once its time is over : until the new one is there, the old one takes the requests.
  • A group of separate sandboxes restarts after its own time (autoRestart.afterTheseMuchSeconds), or never : not after this one.
  • The sandbox for debugging never restarts : you are looking at it.

Import & Export Tools

The tab Import & Export Tools : the programs of the databases which Import & Export and Automatic Backup run — MongoDB Database Tools, the clients of PostgreSQL, MySQL and MariaDB, SqlPackage. One row per tool : the versions every server installs for your account, the one the jobs run, what this server has, and a version to add.

This server finds by itself the version it runs now of each tool, in __import_export_tools or on its PATH, and shows it first : ✓ with the version and where it comes from — yours, installed by a job (a job installs what it needs on first use ; × removes it from the servers), listed by another admin or on the PATH. With nothing there : installed from the publisher on first use. The page looks again each time the tab is shown, when you come back to the browser tab of the page, and with ⟳ in the title of the card (or Refresh) : a version a job just installed, or a program installed by hand on the server, shows at once.

Tool This server shows, for example
MongoDB Database Tools ✓ 100.13.0 on the PATH
PostgreSQL client ✓ 18.6 installed by a job
MariaDB client, on a Mac No build for darwin-arm64, then ✓ 8.4.13 MySQL client, in its place
SqlPackage installed from Microsoft on first use
  • Automatic names that version in its list (Automatic · 18.6) : what a job runs when its database needs no other version. For each job, Automatic takes the version most compatible with the server of its database : pg_dump of the major version of the PostgreSQL server, the 8.4 client for MySQL 8, mariadb-dump of the line of the MariaDB server, the newest MongoDB tools and SqlPackage. A server which lacks it installs it from its publisher then. See the tools for each one.
  • MariaDB publishes no build for macOS and a few other systems : without mariadb-dump on the PATH, the jobs run the client of MySQL in its place (Automatic · MySQL client), told to leave out what MariaDB does not have.
  • The Import & Export dialog says the same version at its top, and checks it again when a job ends : a program installed on first use shows its version as soon as the export is done.

Allowed origins

The tab Allowed Origins. With one origin listed, a browser request from any other origin is refused with 403 before anything runs. Add the origins of your web apps with + : the list is saved at once, and the tab says how many origins are allowed. Empty : every origin.

What is saved

The settings are part of the admin user (settings.sandbox.environments, sandboxCount, removeSandboxInactiveSinceMinutes, servers, automaticSandboxRestartInSeconds, allowedOrigins, settings.nativeLanguages for the native languages and settings.importExport for the tools), so they have a history (the git button of the page) and travel with the project through git.

settings of src/admin_user/data.yaml
settings:
  sandbox:
    sandboxCount: 0                         # every server : 0 is Auto
    removeSandboxInactiveSinceMinutes: 30   # every server : nothing for the default of each server, 0 never
    servers:                                # what some servers have of their own
      - server: db-box:48246                # the id of the server
        name: server2                       # what it was called : shown while it is away
        sandboxCount: 6                     # nothing for the count of every server, 0 Auto
        removeSandboxInactiveSinceMinutes: 0
    automaticSandboxRestartInSeconds: 0