Skip to content

feat: PostgreSQL sink backend - #1687

Draft
dcoric wants to merge 82 commits into
mainfrom
feat/postgres
Draft

feat: PostgreSQL sink backend#1687
dcoric wants to merge 82 commits into
mainfrom
feat/postgres

Conversation

@dcoric

@dcoric dcoric commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Adds PostgreSQL as a supported sink backend, complete with schema migrations, data migration, and production connection/auth options.

This is an integration branch. The PostgreSQL work was split into small, reviewable PRs that build on each other, and merging them into main one at a time would leave a half-configured backend in place for days at a time. They target this branch instead, so each keeps its own review and CI, and main sees the finished backend in one piece.

Collected PRs

PR Scope Status
#1532 PostgreSQL sink backend (users, repos, pushes, session store) Merged
#1581 Versioned DDL schema migrations with advisory locking Merged
#1582 Data migration from mongo/fs into PostgreSQL In review
#1583 Idle pool client error handling Ready for review
#1584 Configurable connection (discrete fields, TLS, pool tuning) Ready for review
#1590 Normalise repo permissions into a repo_users join table Ready for review
#1591 AWS RDS/Aurora IAM authentication Ready for review
#1692 Pushes hot-path indexes and slimmer list projections Ready for review
#1689 sink-parity agent skill and AGENTS.md steering for backend parity Ready for review

The diff below reflects what has merged so far. This PR stays a draft and fills out as the remaining pieces land; it will be marked ready for review once the backend is complete. Related issues: #1688, #1690, #1691.

What lands

  • postgres sink selectable via the existing sink config, alongside fs and mongo
  • Full Sink interface parity with the mongo and NeDB backends, including getRepoPushRollupsByCanonicalUrl, getPushesForUserProfile, updateRepo and the migration hooks
  • Versioned, advisory-locked DDL migrations applied on startup
  • migrate-to-postgres command for existing mongo/fs deployments
  • Connection via connection string, discrete fields or PG* env vars, with TLS and pool tuning
  • Optional RDS/Aurora IAM token auth
  • Design decisions recorded in the architecture doc, plus a sink-parity skill so future adaptor changes keep all backends aligned
  • Unit plus integration coverage, with a dedicated PostgreSQL CI lane

Note on migrations

main has since grown a cross-backend migration framework in src/db/migrations, which records logical migrations by string id through Sink hooks. That is a different concern from #1581, which versions the postgres DDL itself and serialises concurrent runs with an advisory lock. Both are kept: the postgres adapter implements the framework hooks in src/db/postgres/migrations.ts, and the DDL runner lives alongside it in src/db/postgres/schemaMigrations.ts.

Existing deployments are unaffected: the default sink remains the filesystem backend and postgres is opt-in.

dcoric added 30 commits May 11, 2026 16:13
Stub modules for the upcoming Postgres sink adapter. No behavior yet —
each adapter file is a placeholder so subsequent commits can land each
concern (helper, pushes, repos, users) in isolation.

Refs #1497
Adds a third `oneOf` entry for the `database` definition in the JSON
schema and regenerates the TypeScript config types. `connectionString`
is optional at the schema level so an env-var fallback can supply it at
runtime (added in a follow-up commit).

Refs #1497
Adds `GIT_PROXY_POSTGRES_CONNECTION_STRING` to `serverConfig` and wires
the postgres branch of `getDatabase()` to populate `connectionString`
from it when the user config omits one. Mirrors the existing pattern
used for `GIT_PROXY_MONGO_CONNECTION_STRING`.

Refs #1497
Documents the new sink type in the shipped default config. Disabled by
default so the `fs` backend continues to be selected unless an operator
explicitly enables postgres.

Refs #1497
Runtime deps for the new PostgreSQL sink adapter:

- `pg` — node-postgres client + Pool, used by the adapter modules.
- `connect-pg-simple` — express-session store backed by Postgres,
  used to persist UI sessions when the postgres sink is active.
- `@types/pg`, `@types/connect-pg-simple` — TypeScript definitions.

Refs #1497
Implements the foundation shared by the postgres adapter modules:

- `connect()` lazily constructs a `pg.Pool` from the configured
  connection string and runs an idempotent `CREATE TABLE IF NOT EXISTS`
  bootstrap exactly once per process. All adapter modules acquire the
  pool through this function, so the schema is in place before any
  query is executed against `users` / `repos` / `pushes`.
- `query()` is a thin convenience wrapper that awaits `connect()` and
  delegates to `pool.query`.
- `resetConnection()` tears down the pool and bootstrap latch — used by
  the integration test harness between suites.
- `getSessionStore()` returns a `connect-pg-simple` store bound to the
  same pool. Per issue #1497 it MUST NOT silently return undefined when
  postgres is the active sink (express-session would silently fall back
  to MemoryStore), so a missing connection string throws instead.

The schema covers the three application tables plus the indexes used by
`getPushes` (timestamp DESC) and `getRepo` (name lookup). The session
table is left to `connect-pg-simple` via `createTableIfMissing: true`.

Refs #1497
Implements the `Sink` push methods against the `pushes` table:

- `getPushes`: filters by the same keys the mongo backend supports
  (error/blocked/allowPush/authorised/canceled/rejected/type) via a
  small allow-list mapping, then sorts `ORDER BY timestamp DESC` to
  preserve current backend ordering (issue #1497 must-fix).
- `getPush` / `deletePush`: lookups by `id` PK.
- `writeAudit`: upsert on `id` with the full Action serialized into the
  `data` JSONB column and the projection columns kept in sync. Throws
  `Invalid id` to match mongo behaviour.
- `authorise` / `cancel` / `reject`: read-modify-write through
  `getPush` + `writeAudit`, identical to the mongo flow. `reject`
  assigns `action.rejection = rejection` so the persisted payload shape
  (reason / reviewer / timestamp) matches the existing backends.

The Action class is reconstructed from the `data` JSONB via the
existing `toClass` helper.

Refs #1497
Implements the `Sink` user methods against the `users` table:

- `findUser` / `findUserByEmail` / `findUserByOIDC`: lower-case the
  lookup keys to match the mongo and fs case-insensitivity behaviour.
- `getUsers`: optional username / email filters with the same
  lower-casing; SELECT projects `password` away (matching mongo's
  `.project({ password: 0 })`).
- `createUser`: insert with lower-cased username / email.
- `deleteUser`: delete by lower-cased username.
- `updateUser`: dynamic SET-builder that mirrors mongo's partial
  upsert. Identity is by `_id` when supplied, otherwise by `username`;
  if no matching row exists when keyed on username, a new row is
  inserted so callers can patch-or-create without two round trips.

`_id` is exposed as an opaque string (UUID rendered as text) so the
HTTP/UI contract is unchanged versus the mongo backend (which renders
ObjectId via `.toString()`).

Refs #1497
Implements the `Sink` repo methods against the `repos` table.

Permissions (`canPush` / `canAuthorise`) are stored as a single JSONB
column matching the existing mongo/fs shape, with a TODO marker
pointing at a future migration to a normalized `repo_users` join table
(open question called out in issue #1497).

Notable details:

- `addUser*` use `jsonb_set` + a DISTINCT subquery so re-adding an
  existing user is a no-op, matching the fs adapter's `includes` guard.
- `removeUser*` use `coalesce(..., '[]'::jsonb)` around the
  `array_agg` filter so that removing the last user leaves the array
  as `[]`, not `null` — issue #1497 explicitly requires this and the
  reader path additionally defaults `null` arrays to `[]` for
  belt-and-braces resilience against legacy rows.
- `getRepos` accepts the same query keys as the mongo backend
  (name / project / url) with the same lower-casing on `name`.
- `createRepo` returns the row with `_id` populated (UUID rendered as
  text), matching the mongo backend's contract.

Refs #1497
Adds the `postgres` branch to the runtime `start()` selector so a sink
config of `type: 'postgres'` resolves to the new adapter modules, and
re-exports the full `Sink` surface from `src/db/postgres/index.ts`.

The `getSessionStore` return type on the `Sink` interface and on the
top-level `src/db/index.ts` re-export is widened from
`MongoDBStore | undefined` to `MongoDBStore | Store | undefined`, where
`Store` is the express-session base class — `connect-pg-simple`
extends it. This keeps the existing mongo / fs callers type-compatible.

Refs #1497
Per issue #1497 must-fix: when the active sink is one that promises a
persistent session store (currently `mongo` or `postgres`),
`db.getSessionStore()` returning undefined must NOT silently fall
through to express-session's default `MemoryStore` — that store loses
sessions on every restart and is unsafe in any multi-process
deployment.

`createApp` now resolves the store before registering the session
middleware and throws if a persistent backend produced `undefined`.
The `fs` backend is unaffected: it has always returned `undefined`
deliberately, and falling back to MemoryStore there matches existing
single-node-only fs semantics.
Mocks the `query` export from the postgres helper so the suite runs
without a live database. Covers:

- `getPushes` ordering — asserts the generated SQL contains
  `ORDER BY timestamp DESC` (issue #1497 must-fix).
- `getPushes` column translation — `allowPush` filter maps to the
  `allow_push` snake_case column.
- `getPushes` unknown filter keys are ignored (no spurious WHERE).
- `getPush` returns null when the row is absent.
- `writeAudit` throws `Invalid id` for non-string ids (mongo parity).
- `writeAudit` upserts via `ON CONFLICT (id) DO UPDATE`.
- `reject` writes a serialized Action into the `data` JSONB column
  with the `rejection` field populated — confirming the payload shape
  matches the existing backends.
- `reject` throws when the push is missing.
Covers behaviour-critical paths:

- case insensitivity: findUser / findUserByEmail / createUser /
  deleteUser all lower-case their lookup or stored values (parity
  with the mongo and fs adapters).
- getUsers omits `password` from the projection (mirrors mongo's
  `.project({ password: 0 })`).
- updateUser dispatches on `_id` vs `username`, and when the
  username-keyed UPDATE matches nothing it falls back to INSERT —
  this is the upsert semantics issue #1497 calls for.
- updateUser throws when given neither `_id` nor `username`.
Targets the parity invariants called out in issue #1497:

- `getRepoById` defaults a NULL `users` JSONB to empty arrays — guards
  against legacy or partial rows and matches the fs/mongo contract.
- `getRepo` lower-cases the lookup name.
- `createRepo` serialises the default `{canPush:[],canAuthorise:[]}`
  into the JSONB column and stamps the generated `_id` back onto the
  returned object.
- `addUserCanPush` lower-cases the user value before storing.
- `removeUserCanPush` and `removeUserCanAuthorise` emit a SQL fragment
  that wraps the filtered array in `coalesce(..., '[]'::jsonb)`, so
  the array remains `[]` when the last user is removed — this is the
  explicit must-fix from the issue, and an end-to-end check sits in
  the integration suite.
Mocks the `pg` Pool and `connect-pg-simple` constructors so the suite
can exercise the helper without a real database. Covers:

- `connect()` is concurrency-safe: many parallel calls share one Pool
  and run the bootstrap SQL exactly once.
- the bootstrap SQL creates `users`, `repos`, and `pushes` (assertion
  via regex against the inlined statement).
- bootstrap failure does not permanently latch the helper: the next
  `connect()` retries instead of returning the rejected promise.
- `query()` surfaces the helpful error message when the configured
  connection string is missing.
- `getSessionStore()` throws (not returns undefined) when the
  connection string is missing — the explicit must-fix from issue
  #1497 to prevent a silent MemoryStore fallback.
- `getSessionStore()` constructs the `connect-pg-simple` store with
  `createTableIfMissing: true` and shares the helper's pool.

Full suite: 791 unit tests passing (+27 new), zero regressions.
Adds the scaffolding for postgres-backed integration tests:

- `vitest.config.integration.postgres.ts`: separate vitest config that
  scopes `include` to `test/db/postgres/**/*.integration.test.ts`, sets
  `RUN_POSTGRES_TESTS=true`, points `CONFIG_FILE` at the dedicated
  postgres test config, and uses a single-fork pool so the lazy
  pg.Pool is shared across the suite. Mirrors the shape of
  `vitest.config.integration.ts` for mongo.
- `test/setup-integration-postgres.ts`: connects a `pg.Client`,
  truncates the app tables (and the connect-pg-simple `session` table
  if it exists) between tests, drops them in `afterAll`, and calls
  `resetConnection()` + `invalidateCache()` so each test sees a fresh
  helper state.
- `test-integration.postgres.proxy.config.json`: minimal config with a
  single enabled postgres sink and local auth, so `getDatabase()`
  resolves to postgres without the default fs entry winning first.
- `package.json`: adds `npm run test:integration:postgres`.

No suites yet — added in the next commit.
Parity with the mongo pushes integration suite, gated on
RUN_POSTGRES_TESTS=true (set automatically by
vitest.config.integration.postgres.ts). The suite is skipped in normal
`npm test` runs and only executes against a real Postgres in the
dedicated `npm run test:integration:postgres` task.

The added test that goes beyond the mongo parity:

- `getPushes` returns results in descending timestamp order across
  three rows with deliberately distinct timestamps — exercising the
  must-fix ordering requirement end-to-end against a real database.

Otherwise the assertions mirror the existing mongo integration suite
verbatim so backend parity is verifiable side-by-side.
Parity with the mongo users integration suite, gated on
RUN_POSTGRES_TESTS=true. Mirrors the same case-insensitivity and
filtering assertions, plus one additional test exercising the
upsert-on-username path through `updateUser` end-to-end (the mongo
adapter has this via its `upsert: true`, our postgres adapter
implements it as an `UPDATE … WHERE username` fallback to `INSERT`).

`getUsers` asserts `password` is `null` in list responses rather than
`undefined`: the postgres SELECT projects `NULL::text AS password`,
which round-trips as `null` rather than being elided from the JSON
shape — semantically equivalent to mongo's omission for the API
consumers.
Parity with the mongo repo integration suite, gated on
RUN_POSTGRES_TESTS=true.

The permission-JSONB block is the centrepiece — it exercises the
explicit issue #1497 must-fix end-to-end against a real database:

- starts with empty arrays in the JSONB column.
- adding a user is deduplicated (re-adding does not double-insert).
- removing the *last* user leaves the array as `[]`, not `null`.
- the invariant applies symmetrically to `canAuthorise`.
- removing one user from a multi-user list keeps the rest intact.

Skipped without postgres available: full unit suite is 791 passing,
90 skipped (45 mongo + 18 postgres-pushes + 14 postgres-users + 13
postgres-repo), zero failures, zero regressions.
Adds a `postgres:16` service container to the `build-ubuntu` job and
a new `PostgreSQL Integration Tests` step that runs
`npm run test:integration:postgres` against it. The service uses the
default `postgres` superuser with database `git_proxy_test`, matching
the connection string our adapter and test harness default to.

Per the issue's "Open Questions" section, a single Postgres version
is sufficient for the initial lane; a broader matrix can follow once
the backend has soaked in.

Refs #1497
Documents the new `postgres` backend in the `sink` section of the
architecture reference:

- Lists `postgres` as a supported sink alongside `fs` and `mongo`.
- Shows the minimal config block.
- Documents the `GIT_PROXY_POSTGRES_CONNECTION_STRING` env-var
  fallback.
- Calls out the v1 limitations explicitly (no migration tooling, no
  AWS RDS IAM auth, JSONB permissions, no split PG env vars, fail-
  loudly on missing connection string).

Refs #1497
dcoric added 12 commits July 13, 2026 14:28
# Conflicts:
#	website/docs/architecture/architecture.md
Brings the branch up to date with main, including the new UI (React 19),
vitest 4 and TypeScript 6.

Conflicts resolved:
- package.json / package-lock.json: took main's UI stack and dependency
  versions, kept pg, connect-pg-simple and @types/pg. Dropped
  perfect-scrollbar, react-html-parser and react-router-dom, which the new
  UI replaced.
- src/db/types.ts: kept the session store widened to express-session Store
  (needed by connect-pg-simple) alongside main's new
  getRepoPushRollupsByCanonicalUrl.
- src/db/index.ts: kept main's runMigrations alongside the widened
  getSessionStore and ensureSessionStoreReady.
- src/config/generated/config.ts: kept postgres in DatabaseType and took
  main's new AuthType.

The Sink interface gained several members while this branch was behind, so
the postgres adapter implements them here to keep backend parity:
- getRepoPushRollupsByCanonicalUrl and getPushesForUserProfile, matching the
  mongo and fs semantics (URL canonicalisation happens in Node so the
  results agree across backends)
- updateRepo, a partial update mirroring mongo's $set / $unset behaviour
- deriveCreatedAt, getAppliedMigrations, recordMigration and
  unrecordMigration, backing main's migration framework with a migrations
  table. Postgres keys are random UUIDs and carry no embedded creation
  time, so deriveCreatedAt returns undefined as the fs backend does.
main now ships a cross-backend migration framework (src/db/migrations) that
records logical migrations by string id through Sink hooks, and the postgres
adapter implements those hooks in src/db/postgres/migrations.ts.

That is a different concern from this branch, which versions the postgres DDL
itself and serialises concurrent runs with an advisory lock. Both are kept:

- src/db/postgres/migrations.ts holds the Sink hooks for main's framework
- src/db/postgres/schemaMigrations.ts holds the DDL runner from this branch,
  renamed so the two no longer collide on one filename

The DDL runner replaces the CREATE TABLE bootstrap, so it now also creates the
framework's bookkeeping table as migration v3.
@netlify

netlify Bot commented Aug 24, 2026

Copy link
Copy Markdown

Deploy Preview for endearing-brigadeiros-63f9d0 ready!

Name Link
🔨 Latest commit 74aedea
🔍 Latest deploy log https://app.netlify.com/projects/endearing-brigadeiros-63f9d0/deploys/6a8c4d5fdad2ae00087505d9
😎 Deploy Preview https://deploy-preview-1687.git-proxy.preview.finos.org
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Dependency Review

The following issues were found:
  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ✅ 0 package(s) with unknown licenses.
  • ⚠️ 4 packages with OpenSSF Scorecard issues.
See the Details below.

OpenSSF Scorecard

Scorecard details
PackageVersionScoreDetails
npm/@emnapi/core 1.11.1 🟢 3.6
Details
CheckScoreReason
Maintained🟢 1019 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 10
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Code-Review⚠️ 2Found 6/30 approved changesets -- score normalized to 2
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ 0Project has not signed or included provenance with any releases.
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/@emnapi/runtime 1.11.1 🟢 3.6
Details
CheckScoreReason
Maintained🟢 1019 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 10
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Code-Review⚠️ 2Found 6/30 approved changesets -- score normalized to 2
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ 0Project has not signed or included provenance with any releases.
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/@emnapi/wasi-threads 1.2.2 🟢 3.6
Details
CheckScoreReason
Maintained🟢 1019 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 10
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Code-Review⚠️ 2Found 6/30 approved changesets -- score normalized to 2
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ 0Project has not signed or included provenance with any releases.
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/@napi-rs/wasm-runtime 1.1.4 🟢 5.2
Details
CheckScoreReason
Security-Policy🟢 10security policy file detected
Code-Review⚠️ 2Found 6/23 approved changesets -- score normalized to 2
Maintained🟢 1030 commit(s) and 6 issue activity found in the last 90 days -- score normalized to 10
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Packaging⚠️ -1packaging workflow not detected
License🟢 9license file detected
Fuzzing⚠️ 0project is not fuzzed
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
Signed-Releases⚠️ -1no releases found
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/@tybys/wasm-util 0.10.2 UnknownUnknown
npm/@types/connect-pg-simple 7.0.3 🟢 6.6
Details
CheckScoreReason
Code-Review🟢 9Found 29/30 approved changesets -- score normalized to 9
Packaging⚠️ -1packaging workflow not detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Maintained🟢 1030 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 10
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Security-Policy🟢 10security policy file detected
License🟢 9license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
Pinned-Dependencies🟢 8dependency not pinned by hash detected -- score normalized to 8
Binary-Artifacts🟢 10no binaries found in the repo
Fuzzing⚠️ 0project is not fuzzed
npm/@types/pg 8.23.1 🟢 6.6
Details
CheckScoreReason
Code-Review🟢 9Found 29/30 approved changesets -- score normalized to 9
Packaging⚠️ -1packaging workflow not detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Maintained🟢 1030 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 10
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Security-Policy🟢 10security policy file detected
License🟢 9license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
Pinned-Dependencies🟢 8dependency not pinned by hash detected -- score normalized to 8
Binary-Artifacts🟢 10no binaries found in the repo
Fuzzing⚠️ 0project is not fuzzed
npm/connect-pg-simple 10.0.0 🟢 3.9
Details
CheckScoreReason
Code-Review⚠️ 0Found 1/18 approved changesets -- score normalized to 0
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Binary-Artifacts🟢 10no binaries found in the repo
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Security-Policy🟢 10security policy file detected
Pinned-Dependencies⚠️ 1dependency not pinned by hash detected -- score normalized to 1
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/pg 8.23.0 🟢 5.6
Details
CheckScoreReason
Code-Review🟢 7Found 21/29 approved changesets -- score normalized to 7
Packaging⚠️ -1packaging workflow not detected
Maintained🟢 1028 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/pg-cloudflare 1.4.0 🟢 5.6
Details
CheckScoreReason
Code-Review🟢 7Found 21/29 approved changesets -- score normalized to 7
Packaging⚠️ -1packaging workflow not detected
Maintained🟢 1028 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/pg-connection-string 2.14.0 🟢 5.6
Details
CheckScoreReason
Code-Review🟢 7Found 21/29 approved changesets -- score normalized to 7
Packaging⚠️ -1packaging workflow not detected
Maintained🟢 1028 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/pg-int8 1.0.1 ⚠️ 1.9
Details
CheckScoreReason
Dangerous-Workflow⚠️ -1no workflows found
Code-Review⚠️ 0Found 0/11 approved changesets -- score normalized to 0
Packaging⚠️ -1packaging workflow not detected
Pinned-Dependencies⚠️ -1no dependencies found
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Token-Permissions⚠️ -1No tokens found
SAST⚠️ 0no SAST tool detected
Binary-Artifacts🟢 9binaries present in source code
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
npm/pg-pool 3.14.0 🟢 5.6
Details
CheckScoreReason
Code-Review🟢 7Found 21/29 approved changesets -- score normalized to 7
Packaging⚠️ -1packaging workflow not detected
Maintained🟢 1028 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/pg-protocol 1.16.0 🟢 5.6
Details
CheckScoreReason
Code-Review🟢 7Found 21/29 approved changesets -- score normalized to 7
Packaging⚠️ -1packaging workflow not detected
Maintained🟢 1028 commit(s) and 9 issue activity found in the last 90 days -- score normalized to 10
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/pg-types 2.2.0 🟢 3.9
Details
CheckScoreReason
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Code-Review🟢 4Found 5/12 approved changesets -- score normalized to 4
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
License⚠️ 0license file not detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/pgpass 1.0.5 🟢 3.1
Details
CheckScoreReason
Code-Review⚠️ 1Found 2/18 approved changesets -- score normalized to 1
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained🟢 68 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 6
Binary-Artifacts🟢 10no binaries found in the repo
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Packaging⚠️ -1packaging workflow not detected
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
License⚠️ 0license file not detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/postgres-array 2.0.0 ⚠️ 2.9
Details
CheckScoreReason
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Code-Review⚠️ 1Found 4/23 approved changesets -- score normalized to 1
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Security-Policy⚠️ 0security policy file not detected
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/postgres-bytea 1.0.1 ⚠️ 2.9
Details
CheckScoreReason
Packaging⚠️ -1packaging workflow not detected
Code-Review⚠️ 1Found 1/10 approved changesets -- score normalized to 1
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Binary-Artifacts🟢 10no binaries found in the repo
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Security-Policy⚠️ 0security policy file not detected
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/postgres-date 1.0.7 🟢 3
Details
CheckScoreReason
Code-Review⚠️ 2Found 3/13 approved changesets -- score normalized to 2
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Binary-Artifacts🟢 10no binaries found in the repo
Packaging⚠️ -1packaging workflow not detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
License🟢 10license file detected
Security-Policy⚠️ 0security policy file not detected
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/postgres-interval 1.2.0 🟢 3.1
Details
CheckScoreReason
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
Code-Review⚠️ 1Found 5/26 approved changesets -- score normalized to 1
Packaging⚠️ -1packaging workflow not detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Maintained⚠️ 22 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 2
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Security-Policy⚠️ 0security policy file not detected
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/split2 4.2.0 🟢 4.7
Details
CheckScoreReason
Code-Review🟢 3Found 8/22 approved changesets -- score normalized to 3
Packaging⚠️ -1packaging workflow not detected
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Maintained⚠️ 01 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Security-Policy🟢 10security policy file detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions🟢 9detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Signed-Releases⚠️ -1no releases found
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/tslib 2.8.1 🟢 5.5
Details
CheckScoreReason
Maintained⚠️ 00 commit(s) out of 30 and 1 issue activity out of 30 found in the last 90 days -- score normalized to 0
Code-Review🟢 7GitHub code reviews found for 23 commits out of the last 30 -- score normalized to 7
CII-Best-Practices⚠️ 0no badge detected
Vulnerabilities🟢 10no vulnerabilities detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1no published package detected
Token-Permissions⚠️ 0non read-only tokens detected in GitHub workflows
License🟢 10license file detected
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies🟢 9dependency not pinned by hash detected -- score normalized to 9
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 10security policy file detected
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Dependency-Update-Tool⚠️ 0no update tool detected
Fuzzing⚠️ -1internal error: internal error: Client.Search.Code: Search.Code: GET https://api.github.com/search/code?q=github.com+microsoft+tslib+repo%3Agoogle%2Foss-fuzz+in%3Afile+filename%3Aproject.yaml: 400 []
npm/typescript 5.9.3 🟢 8.1
Details
CheckScoreReason
Code-Review🟢 10all changesets reviewed
Maintained🟢 1030 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Security-Policy🟢 10security policy file detected
Packaging⚠️ -1packaging workflow not detected
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Dependency-Update-Tool🟢 10update tool detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies🟢 9dependency not pinned by hash detected -- score normalized to 9
Signed-Releases⚠️ 0Project has not signed or included provenance with any releases.
SAST🟢 10SAST tool is run on all commits
License🟢 10license file detected
Vulnerabilities⚠️ 19 existing vulnerabilities detected
Fuzzing🟢 10project is fuzzed
Branch-Protection⚠️ -1internal error: error during GetBranch(release-5.9): error during branchesHandler.query: internal error: githubv4.Query: Resource not accessible by integration
CI-Tests🟢 108 out of 8 merged PRs checked by a CI test -- score normalized to 10
Contributors🟢 10project has 36 contributing companies or organizations
npm/xtend 4.0.2 ⚠️ 2.8
Details
CheckScoreReason
Dangerous-Workflow⚠️ -1no workflows found
Token-Permissions⚠️ -1No tokens found
Maintained⚠️ 0project is archived
Packaging⚠️ -1packaging workflow not detected
Code-Review🟢 5Found 11/22 approved changesets -- score normalized to 5
Pinned-Dependencies⚠️ -1no dependencies found
Binary-Artifacts🟢 10no binaries found in the repo
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0

Scanned Files

  • package-lock.json

@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.06%. Comparing base (449f35b) to head (9370ece).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1687   +/-   ##
=======================================
  Coverage   86.06%   86.06%           
=======================================
  Files         100      100           
  Lines        5548     5548           
  Branches      988      988           
=======================================
  Hits         4775     4775           
  Misses        525      525           
  Partials      248      248           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

dcoric added 6 commits August 24, 2026 10:46
CI installs with node 22 / npm 10, whose resolver nests a typescript 5.9.3
under vite-tsconfig-paths. The lock was regenerated with npm 11, which
dedupes that entry away, so npm ci failed on every runner. Regenerating
with npm 10.9.8 satisfies both npm 10 and npm 11.
main added dateCreated/lastModified to Repo and a startup migration
(#1681) backfills them through sink.updateRepo. The postgres adapter
dropped both fields from its update allowlist, which made that
migration throw and abort startup, and reads never returned the dates
so the backfill would re-run every boot.

Bring the adapter to parity with the mongo and fs backends: TEXT
columns on repos, defaults on create, both fields updatable, dates
returned from all reads, and last_modified bumped on permission
changes.
dcoric and others added 5 commits August 24, 2026 13:02
… doc

Documents the deliberate choices behind the adapter so reviewers can
see the rationale in one place: pushes kept as JSONB documents with
typed filter columns, typed rows with JSONB edges for users and repos,
server-generated UUID ids, ISO-8601 string timestamps, mongo-matching
case rules, best-effort email uniqueness, connect-pg-simple sessions,
and loud startup failure when no connection resolves.
feat: add PostgreSQL as a supported sink backend
feat: versioned schema migration runner for the PostgreSQL sink
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants