180 lines
7.5 KiB
Markdown
180 lines
7.5 KiB
Markdown
# Phonograph
|
|
|
|
A friendly, collaborative PostgreSQL derivative for nerds of all stripes.
|
|
|
|

|
|
|
|
# Development Quickstart
|
|
|
|
The Phonograph repository comes with a [mise-en-place](https://mise.jdx.dev/)
|
|
configuration to automatically manage the development environment:
|
|
|
|
- `mise install` installs language runtimes and tooling.
|
|
- `mise run build-css && mise run build-svelte` builds browser assets.
|
|
- `mise run docker-services` runs a Postgres container for local development.
|
|
- `mise run server` compiles and runs the server with the dev profile.
|
|
|
|
## Configuration
|
|
|
|
Refer to [the .env.example file](./.env.example) for configuration options.
|
|
|
|
# LLM Code Policy
|
|
|
|
Large language model code generation is permitted sparingly in very limited
|
|
cases, for example for completing clearly defined transformations which span
|
|
multiple files and are not supported by conventional code actions. All code
|
|
generated by LLMs must be thoroughly and frequently reviewed by the author,
|
|
before committing affected work.
|
|
|
|
As of this writing, models display a strong bias towards patterns which are well
|
|
represented in public open source projects. This can cause them to tend towards
|
|
suboptimal one-size-fits-most or simply outdated coding practices in certain
|
|
circumstances. LLM assistance should be sufficiently constrained to avoid
|
|
allowing outputs to dictate or implicitly guide significant design decisions.
|
|
|
|
Furthermore, current language models broadly behave adversarily, in the sense
|
|
that they are optimized to make perceiving model outputs versus non-model
|
|
outputs as difficult as possible. This can make generated code uniquely
|
|
challenging to review effectively. In this context, non-trivial business logic,
|
|
particularly logic with security implications, may not be implemented with
|
|
direct assistance from LLM tools.
|
|
|
|
Examples of LLM-assisted changes in practice:
|
|
|
|
- Replacing SVG icons with similar webfont icons from a different icon pack.
|
|
(Revision `ztrnxzqv` (Git `a8dd49f7`))
|
|
|
|
# The Phonograph Authorization Model
|
|
|
|
Postgres provides a sophisticated role based access control (RBAC) system, which
|
|
Phonograph leverages to apply permissions consistently across the web UI and
|
|
inbound PostgreSQL[^1] connections.
|
|
|
|
In order to efficiently pool database connections from the application server,
|
|
most actions initiated via the web UI are run as the corresponding database user
|
|
role using the
|
|
[`SET ROLE` command](https://www.postgresql.org/docs/current/sql-set-role.html).
|
|
`SET ROLE` **does not** provide great insulation against privilege escalation.
|
|
**Queries which are not thoroughly validated and escaped must only be run via a
|
|
dedicated connection initiated with the user-level role's credentials.**
|
|
|
|
Given complete freedom it is possible, in fact easy, to configure a Postgres
|
|
table into what would be considered an "invalid" state by Phonograph, so table
|
|
creation and ownership is restricted to the "root" Phonograph role, which acts
|
|
on the behalf of the user in order to facilitate schema updates via the web
|
|
interface.
|
|
|
|
## Permissions Granted via User Roles
|
|
|
|
### Accessing workspace databases
|
|
|
|
`GRANT CONNECT ON <database> TO <role>;`
|
|
|
|
This permission is granted when initially creating the workspace, as well as
|
|
when accepting an invitation to a table.
|
|
|
|
Access to workspaces is controlled via the `CONNECT ON DATABASE` permission.
|
|
However, it is unreasonable to query every backing database to compute the set
|
|
of workspaces to which a user has access, so Phonograph caches workspace-level
|
|
"connect" permissions in its own centralized table (`workspace_memberships`).
|
|
|
|
`workspace_memberships` rows are added whenever the `GRANT CONNECT` command is
|
|
run, and are deleted after a `REVOKE CONNECT` command is run.
|
|
|
|
It is possible that an error occurs after `REVOKE CONNECT` but before the
|
|
membership record is deleted. Therefore for authorization purposes, membership
|
|
of a workspace is not a guarantee that the user has `CONNECT` privileges, just
|
|
that they might. In cases where the root Postgres user is fetching potentially
|
|
sensitive data on behalf of a user, the user's actual ability to connect to the
|
|
database should always be confirmed.
|
|
|
|
### Accessing the `phono` schema
|
|
|
|
`GRANT USAGE ON <schema> TO <role>;`
|
|
|
|
This permission is granted when initially creating the workspace, as well as
|
|
when accepting an invitation to a table.
|
|
|
|
## Creating, updating, and deleting columns
|
|
|
|
`GRANT <table owner role> TO <role>;`
|
|
|
|
This permission is granted when initially creating the table, as well as when
|
|
accepting an invitation to a table, if the invitation includes "owner"
|
|
permissions.
|
|
|
|
**This permission is only used via the web UI and must not be granted to service
|
|
credentials, lest users alter table structure in unsupported ways.**
|
|
|
|
### Reading table data
|
|
|
|
`GRANT SELECT ON <table> TO <role>;`
|
|
|
|
This permission is granted when initially creating the table, as well as when
|
|
accepting an invitation to the table.
|
|
|
|
Phonograph uses `SELECT` permissions to infer whether a table should be
|
|
accessible to a user via the web UI.
|
|
|
|
### Inserting rows
|
|
|
|
`GRANT INSERT (<columns>) ON <table> TO <role>;`
|
|
|
|
Write-protected columns (`_id`, etc.) are excluded.
|
|
|
|
This permission is granted when initially creating the table, as well as when
|
|
accepting an invitation to the table, if the invitation includes "edit"
|
|
permissions.
|
|
|
|
These permissions must be updated for each relevant user role whenever a column
|
|
is added; this is simplified by maintaining a single "writer" role per table.
|
|
|
|
### Updating rows
|
|
|
|
`GRANT UPDATE (<columns>) ON <table> TO <role>;`
|
|
|
|
Write-protected columns (`_id`, etc.) are excluded.
|
|
|
|
This permission is granted when initially creating the table, as well as when
|
|
accepting an invitation to the table, if the invitation includes "edit"
|
|
permissions.
|
|
|
|
These permissions must be updated for each relevant user role whenever a column
|
|
is added; this is simplified by maintaining a single "writer" role per table.
|
|
|
|
## Actions Facilitated by Root
|
|
|
|
- Creating tables
|
|
|
|
## Service Credentials
|
|
|
|
Direct user PostgreSQL connections are performed using secondary `LOGIN` roles
|
|
created by the user's primary workspace role (where the primary workspace role
|
|
is e.g. `usr_{user_id}`). The credentials for these secondary roles are referred
|
|
to as "service credentials" or "PostgreSQL credentials". Service credentials are
|
|
created and assigned permissions by users in the web UI, and their permissions
|
|
are revoked manually in the web UI and/or by cascading `REVOKE` commands
|
|
targeting the primary workspace role.
|
|
|
|
Service credential role names have the format
|
|
`svc_{user_id}_{8 chars (4 bytes) of random hex}`. With the user ID consuming 32
|
|
characters, this balances name length with an ample space for possible names.
|
|
|
|
# Copyright and License
|
|
|
|
All original source code in this repository is copyright (C) 2025 Second System
|
|
Technologies LLC and distributed under the terms in
|
|
[the "LICENSE" file](./LICENSE). Certain third-party assets within
|
|
[the "static" directory](./static) may be governed by different licenses, for
|
|
example the Open Font License or MIT License, as stated by their original
|
|
authors. Copies of each relevant license have been included alongside these
|
|
files as needed.
|
|
|
|
# Footnotes
|
|
|
|
[^1]:
|
|
Barring historical pedantry, "Postgres" and "PostgreSQL" are essentially
|
|
synonymous and are often used interchangeably. As a matter of convention
|
|
throughout Phonograph docs, "Postgres" is largely used to refer to the
|
|
database software, while "PostgreSQL" is typically used to refer to the
|
|
query language and/or wire protocol.
|