{"id":30045634,"url":"https://github.com/seanwevans/pg_browser","last_synced_at":"2026-02-11T10:01:28.664Z","repository":{"id":308192160,"uuid":"1031961212","full_name":"seanwevans/pg_browser","owner":"seanwevans","description":"A browser in postgres","archived":false,"fork":false,"pushed_at":"2025-09-26T15:29:29.000Z","size":251,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-26T17:35:40.231Z","etag":null,"topics":["browser","database-experiment","pgsql","postgresql","tooling"],"latest_commit_sha":null,"homepage":"","language":"PLpgSQL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seanwevans.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-04T15:36:27.000Z","updated_at":"2025-09-26T15:29:31.000Z","dependencies_parsed_at":"2025-09-26T17:22:32.213Z","dependency_job_id":"370ed648-9d52-4e2b-8b21-bb20a949661d","html_url":"https://github.com/seanwevans/pg_browser","commit_stats":null,"previous_names":["seanwevans/pg_browser"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/seanwevans/pg_browser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanwevans%2Fpg_browser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanwevans%2Fpg_browser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanwevans%2Fpg_browser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanwevans%2Fpg_browser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seanwevans","download_url":"https://codeload.github.com/seanwevans/pg_browser/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seanwevans%2Fpg_browser/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29331592,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-11T06:13:03.264Z","status":"ssl_error","status_checked_at":"2026-02-11T06:12:55.843Z","response_time":97,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["browser","database-experiment","pgsql","postgresql","tooling"],"created_at":"2025-08-07T07:54:06.309Z","updated_at":"2026-02-11T10:01:28.606Z","avatar_url":"https://github.com/seanwevans.png","language":"PLpgSQL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pg_browser\n\u003cimg width=\"256\" alt=\"no thanks just browing\" src=\"https://github.com/user-attachments/assets/cac66346-28c0-46c5-8798-8f1c50829161\" /\u003e\n\n*A tiny, database-native browser engine built entirely in PostgreSQL (schemas, tables, stored procedures, and extensions).*\n\n`pg_browser` renders extremely simple apps and sites that only need a textbox, a button, and a display area—perfect for chatbots, forms, logs, dashboards, and terminal-style utilities. There is **no JavaScript engine, no CSS**, and no traditional DOM. Everything—fetching, parsing, layout, events, state, history, caching, and rendering—happens *inside* PostgreSQL.\n\n\u003e Status: **Pre-alpha** (design + initial schemas). We’ll iterate toward a usable MVP that renders a JSON UI (“PG-UI”) via an ASCII framebuffer.\n\n---\n\n## Why?\n\n- **Deterministic \u0026 replayable**: Every event and network response is a row. Rewind sessions, diff frames, reproduce bugs.\n- **DB = OS**: State, navigation, layout, and events are first-class data; transitions are stored procedures.\n- **Minimal surface**: A handful of components (`text`, `pre`, `input`, `button`, `list`) and simple `vbox`/`hbox` layout.\n- **Auditable**: No hidden runtime. Every render step is inspectable SQL.\n\n---\n\n## Core Concepts\n\n- **PG-UI (JSON)**: A tiny, declarative UI format served by “sites” (or embedded locally). Example:\n  ```json\n  {\n    \"type\": \"vbox\",\n    \"children\": [\n      {\"type\": \"text\", \"value\": \"Chat\"},\n      {\"type\": \"list\", \"id\": \"messages\", \"items\": []},\n      {\n        \"type\": \"hbox\",\n        \"children\": [\n          {\"type\": \"input\", \"id\": \"msg\", \"placeholder\": \"Type message…\"},\n          {\n            \"type\": \"button\",\n            \"label\": \"Send\",\n            \"on\": {\n              \"click\": {\n                \"post\": \"pgb://local/chat\",\n                \"body\": {\"text\": \"@msg\"},\n                \"append\": {\"target\": \"messages\", \"from\": \"response.text\"}\n              }\n            }\n          }\n        ]\n      }\n    ]\n  }\n  ```\n- **Event loop**: Keystrokes and clicks enqueue events; a dispatcher executes actions (HTTP POST or local stored proc), applies state deltas, re-layouts, and re-renders, then `NOTIFY`s the session channel.\n- **Renderers**: Start with an **ASCII framebuffer** (rows of box-drawn text). Future: PNG bytea renderer.\n\n---\n\n## Features (MVP target)\n\n- Session + history + state (jsonb)\n- PG-UI ingestion (JSON only)\n- Components: `text`, `pre`, `input`, `button`, `list`, `spacer`\n- Layout: `vbox`/`hbox`, fixed font metrics, gaps/padding/alignment\n- ASCII renderer (`render_ascii(session_id)`)\n- Event queue + dispatcher (`input`, `click`)\n- Networking: GET/POST for `application/json` and `application/pgui+json` (via extension)\n- Cache (ETag/If-Modified-Since), per-session cookie jar (P1)\n- `NOTIFY` on new frame\n\n---\n\n## Non-Goals (for now)\n\n- Full HTML/CSS\n- Arbitrary JavaScript execution\n- Complex text shaping; variable fonts\n- Mixed media (images/audio/video) beyond a minimal PNG renderer later\n\n---\n\n## Requirements\n\n- PostgreSQL **16+**\n- PL/pgSQL (built-in)\n- Optional: an HTTP client extension (e.g., `pgsql-http`) **or** a custom C extension for HTTP(S)\n- `uuid-ossp` (or `gen_random_uuid()` via `pgcrypto`) for IDs\n\n---\n\n## Install (planned)\n\nWhen the schemas are published:\n\n```bash\npsql -d yourdb -f sql/00_install.sql\npsql -d yourdb -f sql/10_pgb_net.sql\npsql -d yourdb -f sql/20_pgb_dom.sql\npsql -d yourdb -f sql/30_pgb_layout.sql\npsql -d yourdb -f sql/40_pgb_view.sql\npsql -d yourdb -f sql/50_pgb_events.sql\npsql -d yourdb -f sql/60_pgb_session.sql\n```\n\nOptional:\n\n```sql\n-- If you choose to use pgsql-http:\nCREATE EXTENSION IF NOT EXISTS http;\n```\n\n### Upgrade\n\nFor installations created before the `pgb_session.history.n` column used\n`BIGINT`, run the upgrade script:\n\n```bash\npsql -d yourdb -f sql/61_pgb_session_history_bigint.sql\n```\n\nThis script redefines `pgb_session.reload` by including\n`sql/60_pgb_session_reload.sql`, keeping the function's definition in one\nplace. Modify `sql/60_pgb_session_reload.sql` if the function body needs to\nchange.\n\n---\n\n## Quickstart (design preview)\n\nBelow is how the P0 interface is intended to be used once implemented.\n\n1) **Create a session and open a page**\n\n```sql\n-- A session creates state, history, and an event channel. URLs may include path,\n-- query (?foo=bar), and fragment (#section) components.\nSELECT pgb_session.open('pgb://local/demo_chat') AS session_id;\n-- → returns UUID\n```\n\n2) **Reload the current page (optional)**\n\n```sql\nSELECT pgb_session.reload(:session_id);\n```\n\n3) **Render the initial frame (ASCII)**\n\n```sql\nSELECT line_no, text\nFROM pgb_view.render_ascii(:session_id);  -- rows of text representing the UI\n```\n\n4) **Type a message and click Send**\n\n```sql\n-- User types into input#msg\nSELECT pgb_events.input(:session_id, 'msg'::uuid, 'hello');\n\n-- User clicks the Send button\nSELECT pgb_events.click(:session_id, 'send_button'::uuid);\n```\n\n5) **Receive updated frame**\n\n```sql\n-- Client listens for NOTIFY 'pgb_frame_ready,\u003csession_id\u003e'\n-- Then pulls:\nSELECT line_no, text FROM pgb_view.render_ascii(:session_id);\n```\n\n6) **Replay (debugging)**\n\n```sql\n-- Rewind to a prior timestamp and re-render:\nSELECT pgb_session.replay(:session_id, '2025-08-04T15:30:00Z'::timestamptz);\nSELECT * FROM pgb_view.render_ascii(:session_id);\n```\n\n\u003e **Note:** The `pgb://local/demo_chat` origin maps to a stored procedure (no networking needed) so you can test end-to-end without HTTP.\n\n---\n\n## Security Model\n\n- **Origin allowlist** per session\n- **Strict MIME allowlist** (`application/pgui+json`, `application/json`, `text/plain`)\n- **Sandboxed HTML-lite** (optional P1) — strictly parsed and converted to PG-UI\n- **Size/time limits** on fetches; **cookie jar** is scoped per session\n- **No script execution** of any kind\n\n---\n\n## Project Structure (planned)\n\n```\nsql/\n  00_install.sql\n  10_pgb_net.sql\n  20_pgb_parse.sql\n  20_pgb_dom.sql\n  30_pgb_layout.sql\n  40_pgb_view.sql\n  50_pgb_events.sql\n  60_pgb_session.sql\n  90_devtools.sql\nexamples/\n  demo_chat.pgui.json\ndocs/\n  README.md\n  ROADMAP.md\n```\n\n---\n\n## Testing\n\nA minimal regression harness lives under `tests/`. Run `tests/run_regress.sh` to\nexecute the pg_regress test suite (requires a local PostgreSQL server\naccessible as the `postgres` superuser). The script uses `pg_config` to locate\n`pg_regress`; install the PostgreSQL development package (e.g.,\n`postgresql-server-dev-16` on Debian/Ubuntu) if `pg_config` is not available.\nAdditional integration tests live in the same directory.\n\n## Error Codes\n\nThe project defines custom `SQLSTATE` values. Keep this list up to date when\nintroducing new codes.\n\n| Code   | Raised when |\n|--------|-------------|\n| `PGBUV` | A URL is empty or fails validation in `pgb_session.validate_url` and functions that call it. |\n| `PGBSN` | A session ID does not match an existing session (`pgb_session.navigate`, `pgb_session.reload`, `pgb_session.replay`, `pgb_session.close`). |\n| `PGBNS` | A snapshot for the requested session and timestamp cannot be found (`pgb_session.replay`). |\n\n---\n\n## Contributing\n\n- File issues for design/API changes before implementation.\n- Add **golden frame tests** for any renderer change (snapshot the ASCII output).\n- All new functions require:\n  - spec docstring,\n  - example usage,\n  - unit test (where applicable),\n  - migration script.\n- Document new `SQLSTATE` codes in the **Error Codes** section of this README.\n\n---\n\n## CI/CD\n\nAutomated GitHub Actions run PostgreSQL schema checks on every push and package\nSQL files into a release archive when tags matching `v*` are created.\n\n---\n\n## License\n\nDistributed under the MIT License. See [LICENSE](LICENSE) for details.\n\n---\n\n## Acknowledgments\n\n- PostgreSQL community \u0026 extension authors.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseanwevans%2Fpg_browser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseanwevans%2Fpg_browser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseanwevans%2Fpg_browser/lists"}