HTTP and WebSocket API¶
The canonical machine-readable contract is openapi.yaml. Paths below are relative to the service origin; when BASE_PATH=/postgis-service, both /postgis-service/... and unprefixed forms are routed by the application. JSON responses use application/json; charset=utf-8. Error JSON is generally {"error":"..."}, although middleware and WebSocket pre-upgrade errors use plain text.
All routes require authentication and a service permission; see Authentication. There is no API version prefix, pagination token, idempotency key, or documented stability policy in source.
Browser and asset routes¶
| Methods | Path | Behavior |
|---|---|---|
GET |
/ |
302 to /databases with request base path |
GET |
/login |
303 to Central login; global auth middleware runs first |
GET, HEAD |
/workspace, /connections, /databases, /jobs, /users, /users/detail, /groups, /groups/detail, /network-access, /settings, /terminal |
Render HTML |
GET, HEAD |
/databases/{id} |
Render database-detail HTML; ID is passed to the template without integer validation |
GET, HEAD |
/app.js, /style.css, /terminal.js |
Serve local assets with modification-time revalidation |
| Any matched method | /shared/ui/{path} |
Delegated to the sibling shared UI handler; exact files/method behavior is owned by that handler, not this service |
Connections and target-server access¶
| Method | Path | Input / result |
|---|---|---|
GET |
/api/connections |
Connection array; never returns passwords |
POST |
/api/connections |
{name,host,port,sslmode,admin_username,admin_password} → 201 {id} |
GET |
/api/connections/{id} |
One connection |
PUT |
/api/connections/{id} |
Same body; blank password retains existing password |
DELETE |
/api/connections/{id} |
Deletes metadata and cascaded database/rule rows, not the PostgreSQL server |
POST |
/api/connections/{id}/test |
Pings postgres; returns ok and server version |
GET |
/api/connections/{id}/databases |
Live non-template, connectable database names |
POST |
/api/connections/{id}/postgis-test |
Optional {database} (default postgres) → PostGIS status/version |
PUT |
/api/connections/{id}/hba-path |
{hba_file_path} persists path |
GET, POST |
/api/connections/{id}/pg-users |
List login roles / create {name,password,can_login?,is_superuser?} |
PUT, DELETE |
/api/connections/{id}/pg-users/{name} |
Update optional password/login/superuser flags / drop role |
GET |
/api/connections/{id}/pg-users/{name}/access |
Per-database direct/inherited access matrix |
GET, POST |
/api/connections/{id}/pg-groups |
List non-login roles / create {name} |
DELETE |
/api/connections/{id}/pg-groups/{name} |
Drop group role |
GET |
/api/connections/{id}/pg-groups/{name}/access |
Group database access matrix |
POST |
/api/connections/{id}/pg-groups/{name}/members |
{member} → grant group to role |
DELETE |
/api/connections/{id}/pg-groups/{name}/members/{member} |
Revoke membership |
Role path names are URL-decoded and validated as safe PostgreSQL identifiers before mutation. Reserved roles cannot be dropped. Database access levels are readonly, readwrite, and admin; bundles apply to all currently grantable non-system schemas and alter default privileges for the connected administrator.
Network access and settings¶
| Method | Path | Input / result |
|---|---|---|
GET, POST |
/api/connections/{id}/access-rules |
List rules/path / create rule |
PUT, DELETE |
/api/connections/{id}/access-rules/{ruleId} |
Replace or delete a stored rule |
POST |
/api/connections/{id}/access-rules/apply |
Backup and replace managed HBA block, then pg_reload_conf() |
GET |
/api/settings?connection_id={id} |
Curated settings groups and restart/modified counts |
POST |
/api/settings/update |
{connection_id,name,value} → ALTER SYSTEM SET and reload |
HBA rule body fields are name, cidr, auth_method, database_name, role_name, and optional enabled (default true). Auth methods are only scram-sha-256 and md5; trust is forbidden. An all-address rule with empty database and role scope is rejected. See Configuration for settings validation.
Databases and data¶
| Method | Path | Input / result |
|---|---|---|
GET |
/api/databases?connection_id={id} |
Enriched registered databases; zero/invalid filter means all |
POST |
/api/databases |
{connection_id,name,purpose?,enable_postgis?} → 202 database/job IDs |
GET |
/api/databases/{id} |
Registry, connection, live size/PostGIS, table count, geometry/SRID summary |
DELETE |
/api/databases/{id} |
{confirm_name} → queued deletion |
POST |
/api/databases/{id}/clone |
{name} → queued same-connection template clone |
POST |
/api/databases/{id}/backup |
Queue custom-format backup |
GET |
/api/databases/{id}/backups |
Artifact metadata array, newest filename first |
GET |
/api/databases/{id}/backups/{filename} |
Binary artifact download |
POST |
/api/databases/{id}/restore |
{artifact,mode?,new_database_name?}; mode existing (default) or new |
POST |
/api/databases/{id}/import/upload |
Multipart file, maximum body 512 MiB → server source_path |
POST |
/api/databases/{id}/import |
Queue import using {source_path,schema?,table_name?,srid_policy?,layer_name?,overwrite?} |
GET |
/api/databases/{id}/schemas |
Non-system schemas |
GET |
/api/databases/{id}/tables?schema={name} |
Base tables with geometry, SRID, estimate, size, index count |
GET, POST |
/api/databases/{id}/access |
List persisted grants / apply {role_name,level} |
PUT, DELETE |
/api/databases/{id}/access/{name} |
Apply {level} / revoke PostgreSQL and metadata grant |
Upload formats are .gpkg, .zip, .geojson, .json, and .shp. Import accepts those formats; ZIP must contain shapefiles. Schema defaults to public, overwrite defaults true, and accepted SRID policies are effectively blank/preserve/detect or EPSG:<positive integer>. Invalid non-EPSG strings currently behave like preserve rather than returning validation errors.
Restore artifact is a server-side path and must remain under the configured backup directory. Use the backup listing's artifact value; arbitrary client uploads are not accepted by restore.
Jobs¶
| Method | Path | Behavior |
|---|---|---|
GET |
/api/jobs?limit={n} |
Newest first; default 100. A negative/zero value also becomes 100. No maximum is enforced. |
GET |
/api/jobs/{id} |
One job, including payload, result, progress, stage, and errors |
POST |
/api/jobs/{id}/cancel |
Cancels only queued; running jobs return 409 |
See Architecture for worker semantics.
WebSockets¶
These paths are registered without HTTP method restrictions and are intended for WebSocket GET upgrades:
| Path | Query | Function |
|---|---|---|
/api/terminal/ws/psql |
required connection_id; optional database default postgres |
Interactive psql PTY using stored admin credentials |
/api/terminal/ws/server |
optional connection_id and database |
Restricted line-oriented server terminal; DB maintenance commands require a connection |
/api/terminal/ws/logs |
optional positive lines, default 200 and internally clamped |
Follows journalctl -u postgresql |
Binary frames carry terminal input/output. Text frames carry JSON control messages:
Defined control types are resize, pause, resume, filter, ping, pong, error, and info; fields are cols, rows, filter, and message. Logs use pause/resume/filter; PTY bridges use resize and ping/pong where handled. Exact close codes are not defined as a public contract.
The restricted terminal allows only PostgreSQL systemctl status/restart/reload, PostgreSQL journalctl, df -h, free -m, filtered ps aux, vacuumdb, reindexdb, restricted pg_dump, and list-only pg_restore. Shell metacharacters are rejected. See Authentication for origin and admin checks.