Skip to Content
CLI ReferenceConnections

Connections

A connection says one thing: this app may use this service, at this level — the three parts are a site, one of the project’s databases, buckets or auth apps, and the level the connection grants. It is the single record behind everything the deployed app is handed: its connection variables, the network path that lets it reach a database at all, and the capabilities of its managed platform key.

The full model — the levels, what each one grants, and what a new service starts connected to — is in the Connections guide.

CLI v0.11.0 and newer print the variables a connection adds or removes. On an older CLI the commands work the same, they just don’t list the names — ghayma env pull always shows what the app receives. The read-only levels (--level read-only on a database, --level read on a bucket) need v0.11.1 or newer; the console sets them on any CLI.

Multi-site projects: connect and disconnect act on one site, and connections can be narrowed to one with --site <slug> (slug or name). Without it they use the site this directory is linked to, and at a workspace manifest root they ask which site you mean. See Sites.

ghayma connections

List the project’s connections: which site (app) may use which database, bucket or auth app, and at what level. Without --site, every site of the linked project is listed.

# Every site of this project ghayma connections # One site ghayma connections --site admin # The rows exactly as the API returns them ghayma connections --json

Output:

🔗 Connections for my-project: SITE KIND SERVICE LEVEL admin database my-postgres read-only main database my-postgres connect main bucket uploads read-write main auth app shop client
ColumnWhat it holds
SITEThe site’s slug — the app the connection is for
KINDdatabase, bucket or auth app
SERVICEThe service’s name, as the API lists it — a database’s or bucket’s name, an auth app’s app id
LEVELread-only or connect, read or read-write, client or admin

Rows are ordered by site, then by kind (databases, buckets, auth apps), then by name. With nothing connected the command prints No connections yet. and the ghayma connect line below it.

Options:

FlagDescription
--siteOnly this site (name or slug)
--jsonPrint the rows as JSON, exactly as the API returns them

ghayma connect

Connect a site (app) to one of the project’s services. The app then receives the service’s variables, the network path to a database opens, and the app’s managed platform key gains what the connection implies — on the running deployment and at every deploy. The credential it is handed is its own: a database role or MongoDB user created for that one connection rather than the database’s main user, and an S3 access key created for that one connection rather than the bucket’s. See Each app gets its own database credential and Each app gets its own storage key.

ghayma connect database my-postgres ghayma connect bucket uploads --site admin ghayma connect auth shop --level admin

Kinds are database, bucket and auth; db and storage are accepted as aliases for the first two. Names are what the API lists: a database’s or bucket’s name, an auth app’s app id. A name that matches nothing is an error listing the project’s services of that kind, and a project with none of that kind is told how to create one — names are matched against the site’s own view, so a name can never reach another project’s service.

The site is the one this directory is linked to, or --site.

Levels — every kind takes two, and omitting --level grants what a connection has always granted:

KindLevelsDefault
databaseread-only, connectconnect
bucketread, read-writeread-write
authclient, adminclient

A --level the kind does not accept fails before the request is sent.

# The app may read the database, and nothing more ghayma connect database my-postgres --level read-only # GET and LIST on the bucket; PUT and DELETE are refused ghayma connect bucket uploads --level read

A read-only connection injects the same variables and opens the same network path as a full one — only the credential is narrower. See Read-only connections.

auth ... --level admin prints the console’s note first: admin lets this app manage all users of this auth app. At client the site’s managed key cannot touch that app’s end users.

Running the command again with another --level changes the level of the existing connection rather than adding a second one. A service already connected at the level you asked for is left alone.

Output — the command names the variables the connection adds to that app:

✅ Connected database 'my-postgres' to 'main' (connect) The platform now injects into 'main': DATABASE_URL, DATABASE_URL_MY_POSTGRES Variables are live on the running app; pull them locally with: ghayma env pull

Names only — no value is printed. The first database, bucket or auth app a site is connected to also holds the flat names (DATABASE_URL here); every service gets names suffixed with its own, as Automatically injected variables describes.

A level change reports both ends:

✅ Auth app 'shop' on 'main': level client → admin

And an unchanged connection is not an error:

ℹ️ Database 'my-postgres' is already connected to 'main' (connect) — no change.

Options:

FlagDescription
--siteSite (app) to connect, by name or slug
--levelAccess level (database: read-only or connect; bucket: read or read-write; auth: client or admin)

ghayma disconnect

Disconnect a site (app) from one of the project’s services. Its variables leave the app, the network path to a database closes, and the app’s managed platform key loses what the connection implied. The credential created for that app is deleted with it — the database role or MongoDB user, or the bucket’s per-connection access key. The other apps, and the service’s own credential, are untouched.

ghayma disconnect database my-postgres ghayma disconnect auth shop --site admin --yes

The command confirms first, naming the variables that will leave, unless --yes:

⚠️ This stops 'main' using database 'my-postgres': DATABASE_URL, DATABASE_URL_MY_POSTGRES leave the app, and the network path closes. Continue? [y/N]

Anything but y prints ❌ Cancelled. and changes nothing. On a yes:

✅ Disconnected database 'my-postgres' from 'main'

Disconnecting something already disconnected is not an error — the command says so and stops, so a retry after a lost connection is safe.

Options:

FlagDescription
--siteSite (app) to disconnect, by name or slug
--yesSkip the confirmation

ghayma connections rotate

Replace the credential one app holds for one service. The other apps using the same database or bucket keep theirs, and the service’s own shared credential is untouched. The level, the variable names and every other connection stay exactly as they were — only the secret moves. Why and when to do it: Rotating Credentials.

ghayma connections rotate database my-postgres ghayma connections rotate bucket uploads --site admin --yes

Kinds are database and bucket (with the db and storage aliases). An auth app has no per-connection credential, and the command says so before sending anything — rotate its keys with ghayma auth rotate-keys.

The app is the site this directory is linked to, or --site. The command confirms first, unless --yes:

⚠️ This replaces the credential 'main' uses for database 'my-postgres' now: the running app is updated immediately (its pods restart with the new value), and anything using a locally pulled .env.local needs 'ghayma env pull' again. Continue? [y/N]

Anything but y prints ❌ Cancelled. and changes nothing. On a yes:

✅ Rotated the credential of database 'my-postgres' for 'main' Variables are live on the running app; pull them locally with: ghayma env pull

A service that is not connected to the app is not an error — the command says there is nothing to rotate and stops. Two refusals name the way out: a connection served by the service’s shared credential has nothing of its own to rotate (rotate that one with ghayma db rotate or ghayma storage rotate), and a database or bucket that cannot be reached leaves everything as it was, so running the command again once it answers is safe.

Options:

FlagDescription
--siteSite (app) whose credential to rotate, by name or slug
--yesSkip the confirmation

ghayma connections rotate needs CLI v0.11.2 or newer. The console rotates a connection’s credential on any version.

ghayma access

Show who may reach one of the project’s services — the apps inside Ghayma that are connected to it, and the named principals outside Ghayma that hold their own credential for it.

An external principal is a connection minus the app: a name, a level, its own engine credential, an optional source-IP allowlist and an optional expiry. The model, the hostnames it hands out and the allowlist rules are in the External Access guide.

ghayma access database pg-main ghayma access bucket uploads --json

Kinds are database and bucket, with db and storage accepted as aliases. Names are the project’s own — a database’s or bucket’s name, resolved inside the linked project, never another project’s same-named service. External access is project-scoped, so no site is involved and --site is not a flag here.

Output:

🔐 Access to database 'pg-main' Apps in this project APP LEVEL main connect External principals NAME LEVEL ALLOWLIST EXPIRES LAST USED STATUS metabase read-only 10.0.0.0/8 2026-12-01 — active partner-x CI connect any source — — active legacy-etl read-only any source — — revoked ℹ️ 'partner-x CI' has no allowlist, so no IP filter is enforced at the front door while it is in force. Give every principal an allowlist, or expect none of them to be filtered.
ColumnWhat it holds
NAMEThe principal’s display name. A full principal id is accepted too, wherever a command takes a name
LEVELread-only or connect on a database; read or read-write on a bucket
ALLOWLISTThe source CIDRs, or any source
EXPIRESThe date it retires itself, or
LAST USEDEmpty () for databases in this release — their traffic never passes through the platform API
STATUSactive, expired or revoked

In-force principals come first, then alphabetically. No secret ever appears in a listing — there is no column for one and no field to fill it from.

With nothing there, the command names the way to start:

none — add one with: ghayma access add database pg-main --name <principal>

An auth app has no external principals, and the command says so instead of calling anything:

❌ An auth app has no external principals — its external access IS its restricted project keys. Create or revoke one in the console: Project → Settings → API keys (https://dash.ghayma.cloud).

Options:

FlagDescription
--jsonPrint the rows as JSON (connections and external side by side)

The whole ghayma access family needs CLI v0.11.4 or newer. The console manages external access on any version.

ghayma access add

Grant a named principal outside Ghayma its own credential for one database or bucket. The credential is printed once and cannot be retrieved again.

ghayma access add database pg-main --name metabase ghayma access add database pg-main --name "partner-x CI" --level read-only --allow 203.0.113.0/24 ghayma access add bucket uploads --name backups --expires 30

Output — a database principal is handed a ready-made URL on this database’s own hostname:

✅ Added 'metabase' to database 'pg-main' (read-only) Allowed sources: 10.0.0.0/8 Expires: 2026-12-18 🔑 Credential for 'metabase' on database 'pg-main' URI: postgresql://c_1a2b3c4d:****@pg-main-a1b2c3d4.db.ghayma.tech:5432/db_main?sslmode=require Username: c_1a2b3c4d Password: **** ⚠️ this credential is shown once and cannot be retrieved again — store it now. Lost it? Mint a new one with: ghayma access rotate database pg-main metabase

A bucket principal is an endpoint plus a key pair, not a URL:

🔑 Credential for 'backups' on bucket 'uploads' Endpoint: https://s3.ghayma.tech Bucket: st-uploads-a1b2c3d4 Access key: GK31c2f218a4b7 Secret key: ****

A missing --name, and a --level the kind does not accept, are answered locally — nothing is sent:

❌ A principal needs a name: ghayma access add database pg-main --name <principal>

Options:

FlagDescription
--nameName of the principal receiving the credential (required)
--levelAccess level (database: connect|read-only; bucket: read-write|read). Default: the kind’s full level
--allowComma-separated source CIDRs or addresses. Default: any source
--expiresRevoke the principal automatically after this many days. Default: no expiry

Omitting --level grants the kind’s full level (connect, read-write) — the same default a connection has. The console’s add form starts at the weakest instead. Pass --level and neither can surprise you.

ghayma access allow

Replace the source addresses one principal may connect from. The list you pass is the whole truth — it replaces whatever was there, and --set "" clears it.

ghayma access allow database pg-main metabase --set 203.0.113.0/24,198.51.100.7 ghayma access allow bucket uploads backups --set ""
✅ Allowlist for 'metabase' on database 'pg-main': 198.51.100.7/32, 203.0.113.0/24
✅ Allowlist cleared for 'metabase' on database 'pg-main' — any source may use its credential.

Bare addresses are accepted and widened (203.0.113.4 becomes 203.0.113.4/32), and the server canonicalises, dedupes and sorts the list — so the line you get back is what is actually stored. At most 50 entries per principal.

The flag is never implied: leaving --set out entirely is refused rather than read as “clear it”.

❌ Pass the whole list with --set (an empty --set "" clears it): ghayma access allow database pg-main metabase --set <cidr,...>

After the change the command re-states the collapse rule whenever it applies — while any principal of the service is unrestricted, no IP filter is enforced for any of them.

Options:

FlagDescription
--setThe whole allowlist, comma-separated. An empty --set "" clears it (required)

ghayma access rotate

Replace a principal’s secret without moving anything else: the name, the level and the allowlist stay as they are, so the consumer swaps one string and carries on. The old secret stops working immediately.

ghayma access rotate database pg-main metabase ghayma access rotate bucket uploads backups --yes

The command confirms first, unless --yes:

⚠️ This replaces the secret 'metabase' uses for database 'pg-main' now: the old one stops working immediately and whatever holds it has to be updated. Continue? [y/N]

On a yes it prints the new credential exactly as access add does — once.

✅ Rotated 'metabase' on database 'pg-main'

This is also the answer to a lost secret: nothing reads one back, so a replacement is a rotation.

Options:

FlagDescription
--yesSkip the confirmation

ghayma access revoke

Drop the credential one principal holds and close its row. The engine credential is deleted: anything still connecting with it stops at once, and it cannot be restored — the replacement is a new principal with a new secret.

ghayma access revoke database pg-main metabase ghayma access revoke bucket uploads backups --yes
⚠️ This deletes the credential 'metabase' uses for database 'pg-main' now: anything still connecting with it stops immediately, and it cannot be restored. Continue? [y/N]
✅ Revoked 'metabase' on database 'pg-main' — its credential no longer works.

The row itself is kept, marked revoked, so who reached this database, and when did it stop still has an answer. Revoking something already revoked is not an error:

ℹ️ 'metabase' is already revoked on database 'pg-main' — no change.

On a PostgreSQL database, revoking the last active principal also removes its per-database hostname — the front door closes, it is not just re-passworded.

Options:

FlagDescription
--yesSkip the confirmation

ghayma connect --local

Run the app on this machine against the project’s real databases. --local opens a tunnel session for the site, stands a loopback listener in front of every database the site is connected to, and writes the site’s effective variables with those addresses swapped in — so the app you run here is the deployed app, talking to the same data. Nothing is exposed to the internet.

# Tunnel the linked site's databases and write .env.local ghayma connect --local # A specific app, and a file of your own ghayma connect --local --site admin --out .env.development.local

It takes no arguments — the app is the one this directory is linked to, or --site.

Output:

🔌 Tunnel open for 'main' — its databases answer on this machine: my-postgres (postgres) → 127.0.0.1:15432 analytics (mongodb) → 127.0.0.1:15017 Wrote 12 variables to .env.local (hosts point at the listeners; restore with: ghayma env pull) Leave this running; Ctrl-C closes the tunnel.

Leave it running while you develop. Ctrl-C closes the listeners, gives the session back, and prints ✅ Tunnel closed.

What it binds. One listener per connected database, on 127.0.0.1 only: PostgreSQL counts up from 15432, MongoDB from 15017, stepping over a port something else already holds. Buckets and auth apps need no tunnel — they are public HTTPS with credentials, so your machine already reaches them. A site connected to no database is told to connect one first.

The dotenv file is .env.local next to the app — its directory in a workspace — or the path you give with --out. Only each database’s host:port changes; the credentials, the database name and every option in a connection string ride along untouched. The file goes through exactly the same protections as ghayma env pull:

  • a file git tracks is refused outright;
  • inside a repository, a file git does not ignore is refused too unless you pass --force;
  • the file is written readable by you only;
  • no value is ever printed, and the session’s token never reaches the file.

Those addresses answer only while the command runs. The file left behind names 127.0.0.1 ports that stop answering the moment you press Ctrl-C, so it is not a file to deploy or to keep. Run ghayma env pull to write the cluster addresses back.

Opening a tunnel needs the project admin role — the session reaches the databases with the app’s own credentials, the same bar as env pull. A session lasts 8 hours, and both opening and closing it are recorded in the project’s audit log: which site, which project, how many databases, from where — never the token, never a value. A session that expires while you work ends the command with one line telling you to run it again.

Options:

FlagDescription
--siteSite (app) to tunnel, by name or slug
--outFile to write (default: .env.local next to the app)
--forceWrite even if the file is not git-ignored (a tracked file is always refused)

connect --local needs the CLI release that carries it. The platform side is live; if ghayma connect --help does not list --local, update the CLI.

ghayma env pull

Writing a site’s effective variables — the ones you set plus every value the platform derives from its connections — into a local dotenv file is ghayma env pull, on the Environment Variables page.

Relationship to ghayma db sites

ghayma db sites reads and writes the same records from the other side: what it lists is the project’s database connections, and what --add makes is one at the default level, connect. It keeps working exactly as before — ghayma connections and ghayma connect are the newer form, the only one that also covers buckets and auth apps, and the only one that sets a level.