Skip to Content
Framework GuidesConnections

Connections

A connection says one thing: this app may use this service, at this level. It is the single record behind everything a deployed app is handed — its database URL, its bucket credentials, its auth variables, its platform key — and, for a database, the network path that lets it open a connection at all.

A service is not handed to every app of its project. An app reaches a database, a bucket or an auth app once you connect the two, and stops reaching it the moment you disconnect them.

What can be connected

The connectable services are a project’s databases, storage buckets and auth apps. The other end is always a site — one deployable app of the project. The level says how much the connection grants:

ServiceLevelWhat the app gets
Databaseread-onlyEverything connect gives, except that the app’s own credential may only read
DatabaseconnectDATABASE_URL / MONGODB_URI / REDIS_URL — with the MONGODB_URL and VALKEY_URL aliases and the per-database names — plus the network policy that lets its pods, and its builds, reach the database
BucketreadEverything read-write gives, except that the app’s own key may only read the bucket
Bucketread-writeSTORAGE_ENDPOINT, STORAGE_ACCESS_KEY, STORAGE_SECRET_KEY, STORAGE_BUCKET, STORAGE_REGION
Auth appclientGHAYMA_AUTH_APP_ID, GHAYMA_AUTH_URL, GHAYMA_AUTH_PUBLIC_URL and GHAYMA_AUTH_SERVER_KEY, with the app id passed to the build as well
Auth appadminEverything client gives, and the site’s managed GHAYMA_API_KEY may administer that auth app’s end users

A Valkey is the one database a build never reaches: its variables and its network path are runtime only (see Valkey).

Every kind has two levels, listed weakest first. A connection made without a level grants what a connection has always granted — connect for a database, read-write for a bucket, client for an auth app — so the read-only levels are something you ask for. See Read-only connections.

The managed platform key follows the levels

A site connected to a bucket, a database, or an auth app at admin is also handed GHAYMA_API_KEY, a project API key the platform mints for that site. It reaches exactly what the site is connected to, so a project’s second bucket, database or auth app is never swept in: each bucket at its connection’s level, each database to read it, and each auth app connected at admin to administer its end users. Any other one answers 403 with code not_connected, and an object write through a read bucket connection answers 403 with code read_only_connection. A site whose connections imply no access — none at all, or only an auth app at client — gets no key, and any key it still holds is revoked. Deleting the site revokes its key too.

When a connection applies

Connecting, disconnecting or changing a level reaches the running apps right away — the pods pick up the new values without a redeploy, and a database network policy converges within about two minutes. Every later deploy derives the same thing again, so a rollback gets exactly what a deploy gets.

All of these variables are derived, never stored. They do not appear in ghayma env list, there is nothing to edit, and a value you set yourself under one of these names is replaced by the platform’s. See Automatically injected variables.

A database or bucket connection always holds a credential of its own — nothing falls back to the service’s own credential. A connection that has none yet gets no values at deploy time: a deploy carries no value under its variable names. An app that is already running keeps the values it has under those names until the platform gives the connection a credential of its own. The platform retries every ten minutes, as soon as the service runs, and then restarts that app with it.

A deploy of a site with such a connection first tries to give it one. If it still cannot, the deploy stops before building, naming the service and the site:

Database "shop-db" has no credential of its own yet for site "main" — retry in a minute, or reconnect it.

A bucket’s line starts with Bucket. ghayma env pull and ghayma connect --local leave such a connection’s variables out of the file they write.

Each app gets its own database credential

An app connected to a PostgreSQL, MongoDB or Valkey database is never handed the database’s own login. The platform creates a credential for that one connection — named c_ followed by eight characters, like c_14bb37f4 — and that credential is what the app’s DATABASE_URL, MONGODB_URI or REDIS_URL carries.

  • Disconnecting drops only that app’s credential. Every other app on the database, and every named principal, are untouched.
  • Migrations still work across apps. On PostgreSQL every app connected at level connect acts as one shared owner role — <dbname>_rw — so a table created by one app’s migrations can be altered by another app’s. Creating tables, altering tables and types, creating schemas and creating trusted extensions all work as before.
  • Rotating is per app. To re-secret one app’s credential, see Rotating Credentials; the other apps keep theirs. The database’s own login is never handed out, and only an operator rotates it.

On MongoDB the c_… user holds the readWrite role on that one database and nothing else, and it lands in MONGODB_URI and its MONGODB_URL alias.

Nobody is shown the database’s own login — not you, not a collaborator. ghayma db credentials and the console show its host and port and which apps receive which variables, without any password. From your laptop, ghayma connect --local tunnels the site’s databases and writes the app’s own credentials into .env.local, so an app you run on your machine uses the same credential the deployed one does. A system outside Ghayma gets its own too — a named principal is the same c_… identity, with a level, an optional source allowlist and its own revoke.

A connection is made only with a credential of its own. Connecting an app to a database that is stopped or still being provisioned is refused with 409 and code: not_running — start it, or let it finish, then connect again. A service that cannot give an app a credential of its own refuses with 409 and code: credential_unsupported, and the connection is not made. A Valkey that is not running answers code: database_not_running (see Valkey). MongoDB always runs as a single-node replica set, so every MongoDB app gets a user of its own.

Each app gets its own storage key

An app connected to a bucket is handed an S3 access key created for that one connection. It arrives in STORAGE_ACCESS_KEY and STORAGE_SECRET_KEY — and in the suffixed STORAGE_<NAME>_* names — never the bucket’s own key.

  • That key reaches one bucket. It reads and writes that bucket and nothing else, and it can never delete the bucket or change its settings.
  • Disconnecting deletes only that app’s key. The other apps, and every named principal, are untouched.
  • Rotating is per app. To replace one app’s key, see Rotating Credentials; the other apps keep theirs.

Nobody is shown the bucket’s own key. ghayma storage credentials and the console show the bucket’s S3 endpoint and name and which apps receive which variables, without any key. From your laptop, ghayma env pull writes your site’s STORAGE_* variables — the S3 endpoint is public, so they work from your machine as they are. A tool outside Ghayma gets its own key too — a named principal is a key pair of its own, at read or read-write, revocable alone.

Connecting an app to a bucket that is still being provisioned is refused, as for a stopped database, with 409 and code: not_running: connect it once the bucket is active.

Read-only connections

A database connection can be read-only, and a bucket connection can be read. The app keeps the same credential — the same name, the same password or key — and only what it may do changes:

  • PostgreSQL. The app’s role may SELECT everything the read-write apps’ shared owner group holds, including tables another app creates later. No INSERT, UPDATE, DELETE or DDL.
  • MongoDB. The app’s user holds the read role on that database instead of readWrite.
  • Valkey. The app’s user may run reads and SUBSCRIBE, and nothing that writes.
  • Buckets. The app’s key may GET and LIST; PUT and DELETE are refused.

The level changes nothing else. The network path to the database is the same, and so is the set of injected variables — a read-only app still receives the full DATABASE_URL or STORAGE_* set, its credential simply cannot write.

Connecting without a level is unchanged: a database connection starts at connect and a bucket connection at read-write. Pick the read-only level when you connect, or change it afterwards — the console and the CLI both change a level in place, without disconnecting first.

ghayma connect database my-postgres --level read-only ghayma connect bucket uploads --level read

Manage them in the console

There are two views of the same rows in the console  — one per app, one per service. Use whichever matches the question you are asking.

What this app uses

Open Project → the site → Connections:

  • Connected services lists the databases, buckets and auth apps this app may use, each with its level and, as chips, the variables the platform injects for it. Change a service’s level here, or Disconnect it.
  • Connect a service lists the project’s other services, each with the variables connecting would add — pick a level, then Connect.

Databases and buckets carry the level picker on connected rows and in the Connect dialog, exactly as auth apps always have.

Those chips are variable names only; no value is ever shown. Read the values with ghayma env pull.

Which apps use this service

The Access section on the database settings page, the bucket page and the auth app page lists every app of the project, connected or not, with the level of each connection. Connect, disconnect and change a level from there.

On a database or a bucket the same table also carries the named principals outside Ghayma — rows badged external rather than app — with their allowlist, expiry and status, and the actions to add, rotate, revoke or re-allowlist one. An auth app’s external access is its restricted project keys, so its Access list shows those instead.

Raising an auth app to admin asks for confirmation, and the control says why: admin lets this app manage all users of this auth app. Disconnecting asks for confirmation too, and names the variables that will disappear from the app.

Seeing connections needs the read role on the project; connecting, disconnecting and changing a level need write or above.

How a service gets connected

Nothing is connected unless you choose it. A new database, bucket or auth app is connected to the sites you pick when you create it, and to none if you pick none — even in a project with a single site. Creating a site, a project’s first one included, connects nothing either: a new site starts with no connections.

You choose in three places:

  • When you create the service. The console’s create form lists the project’s sites under Connect to sites: a lone site is ticked for you, several start unticked, and a project with no site yet notes that you can connect the service after the first deploy. The CLI takes --site <slug> (repeat it for several) or --no-connect, and asks on a terminal — see Connecting it to a site. The API takes connect_site_ids.
  • When you deploy. ghayma deploy offers each service no site uses to the site it deploys, before the build — see Services no site uses yet.
  • Any time after. From the site’s Connections tab, the service’s Access list, or ghayma connect.
ghayma db create shop-db --site main # connected to 'main' as it is created ghayma storage create media --no-connect # connected to nothing for now

A service no site uses is pointed out: the console shows a banner on the project — 2 services aren’t connected to any site — linking each one to its Access list, and an empty Access list reads Not connected to any app yet — pick the apps that should use it.

A new PostgreSQL database connects once it accepts logins

A new PostgreSQL database accepts logins a moment after it is created — usually within a minute. The sites you chose wait for it: the database’s page shows Connecting to main…, ghayma db sites marks them ⏳, and the API lists them in the database’s pending_connections. They connect as soon as it is ready, each with its own credential. If they still haven’t connected after 15 minutes, the database’s connect_error says why.

A new Valkey works the same way: the sites you chose connect once it is running. Connecting it before then is refused with 409 and code: database_not_running (see Valkey).

From the API

The routes live under /api/v1 and take an account token or a dashboard session. Reading needs the project read role, every mutation needs write.

RouteDoes
GET /projects/:id/connectionsEvery connection in the project. Filter with site_id, kind, resource_id
GET /projects/:id/sites/:siteId/connectionsOne site’s connections, plus the services it could still be connected to
PUT /projects/:id/sites/:siteId/connectionsReplace a site’s whole set
POST /projects/:id/sites/:siteId/connectionsConnect one service, or change its level
DELETE /projects/:id/sites/:siteId/connections/:kind/:resourceIdDisconnect one service
GET /projects/:id/connections/unconnectedThe project’s services no site is connected to

Creating a database, bucket or auth app takes connect_site_ids, the sites to connect it to — see Create Database.

A row is the same everywhere:

{ "site_id": "…", "site_slug": "admin", "kind": "auth_app", "resource_id": "…", "resource_name": "main-auth", "level": "client", "env_names": ["GHAYMA_AUTH_APP_ID", "GHAYMA_AUTH_URL", "…"], "created_at": "2026-09-08T10:00:00Z" }

env_names is the exact set of variable names the platform injects into that site for that connection — names, never values. The services a site could still be connected to carry it too, so a UI can say what connecting would add. See Connections.

The older per-service routes — GET/PUT /databases/:id/sites and GET/PUT /storage/:id/sites — keep working and read and write these same connections. Full reference: Connections.

A project API key cannot call these routes — they answer 403. A site’s managed key is minted from its own connections, so a key able to change them could widen what it may do.

From the CLI

ghayma db sites works exactly as before — what it lists and edits is now a database connection, and --add makes one at the default level, connect:

ghayma db sites my-postgres # which apps may use it ghayma db sites my-postgres --add admin # connect the 'admin' app

The dedicated commands cover all three kinds, and read and write these same rows:

# Which apps may use which services ghayma connections ghayma connections --site admin # Connect a service to an app — without --level, the kind's usual level ghayma connect database my-postgres ghayma connect bucket uploads --site admin ghayma connect auth shop --level admin # Connect at a read-only level, or move an existing connection down to one ghayma connect database my-postgres --level read-only ghayma connect bucket uploads --level read # Stop an app using a service (asks first, unless --yes) ghayma disconnect auth shop --site admin # Write the site's effective variables — the derived ones included — to .env.local ghayma env pull # Tunnel the app's databases to this machine and write .env.local pointing at them ghayma connect --local

Full reference: Connections and ghayma env pull.