Harden the PGXN distribution
Tracking issue for everything needed to make pg_durable on
PGXN actually work for the people who find
it there. The first release (0.2.6, 2026-08-24) is live and correct as a
package, but an end-to-end validation on a clean machine found the advertised
install command does not work, the documentation does not cover the PGXN entry
point, and the search index carries our internal development notes.
Three work items, in priority order. Item 1 is a functional blocker; items 2 and
3 are quality.
| # |
Item |
Severity |
| 1 |
pgxn install pg_durable fails on a clean machine |
Blocker |
| 2 |
No documentation for the PGXN install path |
Should fix |
| 3 |
69 documents indexed, including internal prompts and open-problem notes |
Low |
What is already verified working
So this issue is not read as "PGXN is broken". Full end-to-end run on Debian
bookworm, PostgreSQL 17.11 from PGDG, Rust 1.98.0, cargo-pgrx 0.16.1,
pgxnclient 1.3.2, after cargo pgrx init:
| Step |
Result |
pgxn install pg_durable |
success, 4m 32s wall on 6 vCPU including download |
| Installed library |
pg_durable.so, 11,595,832 bytes, release build, in /usr/lib/postgresql/17/lib/ |
| Control file |
default_version = '0.2.6' correctly substituted |
| SQL files |
8 installed — pg_durable--0.2.6.sql plus 7 upgrade paths |
CREATE EXTENSION pg_durable |
success, extversion 0.2.6, 44 df.* functions |
| Background worker |
started, connected, epoch sentinel written, processing loop running |
| Durable function |
df.start('SELECT ''Hello, durable world!''') → instance e2a73657 → completed → {"rows": [{"?column?": "Hello, durable world!"}], "row_count": 1} |
pgxn uninstall pg_durable |
success, all 10 files removed, no build required |
pgxn uninstall twice |
idempotent — pg_durable is not installed for /usr/bin/pg_config; nothing to remove |
The published archive is also byte-identical in Makefile, META.json.in,
scripts/test-make-install.sh, pg_durable.control and Cargo.toml to git tag
v0.2.6 (6799781), and the macOS .dylib fix from #348 shipped in it.
1. Blocker: pgxn install pg_durable fails with $PGRX_HOME does not exist
On a clean machine with PostgreSQL 17, Rust and cargo-pgrx all installed, the
advertised command fails after 16 seconds:
INFO: building extension
DEBUG: running: ['gmake', 'PG_CONFIG=/usr/bin/pg_config', 'all']
cargo pgrx package --pg-config "/usr/bin/pg_config" ...
Error:
0: $PGRX_HOME does not exist
Location:
pgrx-pg-config-0.16.1/src/lib.rs:582
gmake: *** [Makefile:27: package] Error 1
ERROR: command returned 2: gmake PG_CONFIG=/usr/bin/pg_config all
make all → package → cargo pgrx package, which requires $PGRX_HOME
(~/.pgrx). That directory is created by cargo pgrx init, and pgxn install
never runs it. Everything downstream is fine; this is purely a first-run gate.
It matters because PGXN is now an advertised install channel and is the only
source path for anyone not on Debian or Docker. It is the first thing a PGXN
user hits, and the error names an environment variable that appears nowhere in
our documentation.
The requirement is narrower than it looks
Three probes against the published 0.2.6 archive with cargo-pgrx 0.16.1:
$PGRX_HOME state |
gmake PG_CONFIG=... all |
| absent |
fails — $PGRX_HOME does not exist |
exists, empty (no config.toml) |
fails — /root/.pgrx/config.toml not found. Have you run `cargo pgrx init` yet? |
config.toml present listing only pg18 = "/nonexistent/pg_config" |
compiles successfully |
So the requirement is precisely that $PGRX_HOME/config.toml exists. Its
contents are irrelevant when --pg-config is passed explicitly, which our
package target always does. Two consequences:
-
An existing pgrx configuration pointing at a different PostgreSQL cannot
hijack the build. That was a plausible failure mode; it does not occur.
-
cargo pgrx init --pg<major> <pg_config> is instant when handed an
existing pg_config — it validates and writes config.toml, and does not
download or build PostgreSQL:
Creating PGRX_HOME at `/root/.pgrx`
Validating /usr/bin/pg_config
Skipping initdb as current user is root user
Proposed fix
Follow the ecosystem precedent. theory/pg-jsonschema-boon
— a pgrx extension published on PGXN by PGXN's own author, and the distribution
whose metadata shape we already copied — ships explicit make targets and
documents them, rather than auto-initializing:
.PHONY: install-pgrx # Install the version of PGRX specified in Cargo.toml.
install-pgrx: Cargo.toml
@cargo install --locked cargo-pgrx --version "$(PGRXV)"
.PHONY: pgrx-init # Initialize pgrx for the PostgreSQL version identified by pg_config.
pgrx-init: Cargo.toml
@cargo pgrx init "--pg$(PGV)"="$(PG_CONFIG)"
Its package target is a bare cargo pgrx package --pg-config with no init
guard, so by source reading it has the same $PGRX_HOME dependency we do.
Recommended, in order:
-
Add pgrx-init and install-pgrx targets to our Makefile. We already
compute PG_MAJOR and PG_CONFIG, and Cargo.toml pins pgrx = "=0.16.1",
so both targets are a few lines. This gives the documentation something
concrete to name instead of a raw cargo pgrx init incantation.
-
Make the failure actionable. When $PGRX_HOME/config.toml is absent,
fail before invoking cargo with the exact command to run:
pg_durable: cargo-pgrx is not initialized for this PostgreSQL.
Run: make pgrx-init PG_CONFIG=/usr/bin/pg_config
-
Optionally, guarded auto-init in package. Running
cargo pgrx init --pg$(PG_MAJOR) "$(PG_CONFIG)" when config.toml is absent
would make pgxn install pg_durable work with no user action at all. The
usual objection — that cargo pgrx init can download and compile its own
PostgreSQL — does not apply, because we always pass an explicit pg_config.
It only fires when config.toml is missing, so it cannot clobber an existing
setup. This diverges from the reference distribution, so it is a judgement
call: strictly friendlier, slightly more magic.
Note that pgxnclient passes PG_CONFIG explicitly on both make all and
make install (['gmake', 'PG_CONFIG=/usr/bin/pg_config', 'all']), so there is
no risk of the unprivileged build and the sudo install resolving different
PostgreSQL installations.
2. Document the PGXN install path
PGXN carries the source distribution: the published archive is 970 KB, 337
files, and contains zero .so/.dylib/.dll/.a/.o. Installing means
compiling a Rust extension on the user's machine, which needs the Rust
toolchain, cargo-pgrx 0.16.1, PostgreSQL development headers, and several
minutes. None of that is currently spelled out for someone arriving via
pgxn install.
Today the entire PGXN treatment is one sentence at README.md:162, at the end
of the Packages section, saying PGXN carries the source distribution "built and
installed exactly as described above". It never shows the pgxn command, and
"above" is the tarball recipe, which is a different entry point.
Where it goes
README.md, Packages section — primary. PGXN renders README.md as the
distribution landing page: pgxn.org/dist/pg_durable/ contains an h3: README
followed by the entire README. A PGXN visitor is already reading it.
Replace line 162 with a short "Installing from PGXN" block covering
prerequisites, make pgrx-init (per item 1), pgxn install pg_durable,
pgxn uninstall pg_durable, and a pointer to the Docker image or Debian
packages for anyone who wants a prebuilt binary instead.
USER_GUIDE.md, Getting Started → Prerequisites — cross-reference.
USER_GUIDE.md is the provides.docfile and PGXN links it as "Documentation".
Its Prerequisites currently begin after installation —
shared_preload_libraries, restart, CREATE EXTENSION — so there is a gap
where "how the files got there" belongs. One line pointing at the README is
enough; the install content should not be duplicated.
A separate INSTALL.md is not recommended: it splits install guidance across
three files and is not a PGXN convention.
Relative links in the README break on PGXN
Whatever we write has to survive being rendered on pgxn.org, and today's README
links do not. Verified 404:
https://pgxn.org/dist/pg_durable/USER_GUIDE.md
https://pgxn.org/dist/pg_durable/docs/pg_durable_mvp.md
The PGXN-rendered copy lives at /dist/pg_durable/0.2.6/USER_GUIDE.html (HTTP
200), so the content is reachable — the link is what is broken. Affected
relative links in README.md: USER_GUIDE.md,
USER_GUIDE.md#privilege-grants, docs/pg_durable_mvp.md, SECURITY.md,
docs/TESTING.md#2-pg_regress-tests, examples/README.md, tests/e2e/,
.github/workflows/ci.yml.
Any link intended to work for a PGXN reader needs an absolute URL. Whether to
convert them all, or only the ones in the install path, is a judgement call —
relative links are correct on GitHub, which is the larger audience.
3. Narrow the indexed document set with no_index
The 0.2.6 release indexed 69 documents. PGXN indexes documentation for
full-text search, so the distribution page now advertises, alongside the User
Guide:
| Indexed path |
Title |
prompts/pg_durable-release |
Release Workflow |
prompts/pg_durable-merge-main |
Merge Branch to Main |
.github/copilot-instructions |
pg_durable AI Coding Instructions |
.agents/skills/pg-durable-sql/SKILL |
pg_durable SQL Generation |
docs/dep_issues |
Dependency Issues & Blockers |
docs/http_problems |
Open Problems in the HTTP Activities |
docs/loop_rework_problems |
Open Problems in the Loop Rework |
docs/DUROXIDE_PG_DEADLOCK_ISSUE |
Duroxide-PG Deadlock Issue in Parallel Orchestrations |
docs/CODESPACES_PREBUILDS |
GitHub Codespaces Pre-builds Configuration |
TODO |
TODO |
examples/*/function-app/requirements |
requirements |
Nothing here is secret — the repository is public. The problem is signal: a
reader searching PGXN gets our internal AI development prompts and a list of our
open bugs presented as product documentation, and two requirements.txt pip
manifests are indexed as "documents" because Text::Markup treats .txt as
markup. All seven files under prompts/ are indexed, including the release
runbook describing our own PGXN publishing procedure.
The fix: no_index in META.json.in
The PGXN Meta Spec has a field for exactly this. From
the spec, section no_index:
This Map describes any files or directories that are private to the packaging
or implementation of the distribution and should be ignored by indexing or
search tools.
It is not merely aspirational. Implementation, pgxn/pgxn-api
lib/PGXN/API/Indexer.pm, find_docs() (lines 473-501):
my $skip = { directory => [], file => [], %{ $meta->{no_index} || {} } };
...
for my $member ($p->{zip}->members) {
next if $member->isDirectory;
(my $fn = $member->fileName) =~ s{^$prefix/}{};
next if $seen{$fn}++;
next if first { $fn eq $_ } @{ $skip->{file} };
next if first { $fn =~ /^\Q$_/ } @{ $skip->{directory} };
next unless $markup->guess_format($fn) || $fn =~ /^README(?:[.][^.]+)?$/i;
push @docs => { filename => $fn };
}
Confirmed working in production. pgcollection publishes
no_index: {"directory": [".github"], "file": ["AGENTS.md", "CODE_OF_CONDUCT.md", "CONTRIBUTING.md", "GOVERNANCE.md", "SECURITY.md"]}, and all five files are
absent from its indexed docs while README and doc/* remain. Roughly a
dozen other current distributions use it, including pg_trickle, provsql,
pg_local_cache and pg_clickhouse.
Why not .gitattributes export-ignore
It would work — make pgxn-zip uses git archive, which honours
export-ignore — but it is the wrong instrument:
- It removes the files from the tarball entirely. Someone who downloads the
source distribution to read or build gets a tree that silently differs from
the repository.
- It would also strip the GitHub release assets.
package-release.yml:269
(build-source) builds pg_durable-X.Y.Z.tar.gz and .tar.bz2 with
git archive from the same tree, so one .gitattributes line silently
changes three published artifacts at once. GitHub's auto-generated "Source
code" archives honour it too.
- It solves the wrong problem. We do not want the files gone; we want
them not indexed as product documentation. That is precisely the
distinction no_index draws.
no_index is one field in a file we already generate, affects only indexing and
search, and leaves every artifact byte-identical.
Two implementation traps
1. file entries are matched by exact string equality, with the extension.
The API's docs keys are extension-stripped, so copying them straight out of
https://api.pgxn.org/dist/pg_durable.json does not work:
API docs key |
no_index.file entry required |
docs/dep_issues |
docs/dep_issues.md |
TODO |
TODO.md |
Paths are relative to the archive root — the pg_durable-0.2.6/ prefix is
stripped before matching, so write prompts/README.md, not
pg_durable-0.2.6/prompts/README.md.
2. directory entries are an anchored literal prefix match (/^\Q$_/), not
a path-component match. "docs" would also match a hypothetical docs2/.
Include the trailing slash: "docs/website/".
One useful safety property: provides.<ext>.docfile is collected before the
skip checks run, so USER_GUIDE.md can never be excluded by accident.
Proposed change
Add to META.json.in:
"no_index": {
"directory": [
".agents/",
".github/",
"prompts/",
"docs/website/"
],
"file": [
"TODO.md",
"docs/CODESPACES_PREBUILDS.md",
"docs/DUROXIDE_PG_DEADLOCK_ISSUE.md",
"docs/dep_issues.md",
"docs/http_problems.md",
"docs/loop_rework_problems.md",
"examples/azure-functions/function-app/requirements.txt",
"examples/invoice-approval/function-app/requirements.txt"
]
}
That takes the indexed set from 69 to about 50, removing every entry that is
unambiguously internal: AI prompts and skills, CI instructions, the website
source, the repo TODO, three open-problem notes, the Codespaces config, and two
pip manifests.
Deliberately left indexed: README, USER_GUIDE, CHANGELOG, LICENSE,
SECURITY, CONTRIBUTING, CODE_OF_CONDUCT, the docs/spec-* and
docs/ARCHITECTURE / docs/api-reference / docs/grammar set, and all 13
examples/ documents.
Worth a decision, not obviously either way:
docs/security-review/ (3 documents). Publishing a Microsoft OSS security
review is arguably a credibility asset; workbook-data is raw table data
rather than prose and probably should not be indexed regardless.
- Design and proposal documents —
parameterized_queries_proposal (explicitly
titled "Status: Not Implemented"), proposal-management-api,
nested-graph-design, named-results-v2, move-duroxide-schema,
design-azure-functions. Useful to a contributor, potentially misleading to a
user searching for features that do not exist.
- Test plans —
TESTING, E2E_TESTING, spec-pg-regress, upgrade-testing.
docs/pg_durable_mvp ("pg_durable MVP User Guide") appears superseded by
USER_GUIDE. Two user guides in one search index is worse than one.
Whatever item 2 adds to the README must not be excluded here.
Verification
Items 1 and 2 are testable before release, on a machine with no ~/.pgrx:
pip install pgxnclient
cargo install cargo-pgrx --version 0.16.1 --locked
pgxn install pg_durable
pgxn uninstall pg_durable
Item 3 applies at index time, not bundle time, so make pgxn-zip output is
unchanged and there is nothing to check locally beyond metadata validity:
docker run --rm -v "$PWD:/repo" -w /repo pgxn/pgxn-tools sh -c '
make META.json && pgxn validate-meta META.json'
The real check for item 3 happens after the next release lands:
curl -s https://api.pgxn.org/dist/pg_durable.json \
| python3 -c 'import json,sys; d=json.load(sys.stdin); print(len(d["docs"])); print(*sorted(d["docs"]), sep="\n")'
Suggested CI coverage
No workflow currently invokes pgxn at all. Source Install Checks runs the
offline scripts/test-make-install.sh contract tests, and
macOS Source Install (PG17) calls make package / make install directly
with an explicit PG_CONFIG=. Neither would have caught item 1, because both
runners already have ~/.pgrx.
A job running the four commands above on a clean runner would. Because it
exercises the published distribution it can only run after a release, so a
scheduled or manually dispatched job is more appropriate than a per-PR gate.
Acceptance criteria
- On a clean machine with PostgreSQL 17 or 18, Rust and
cargo-pgrx but no
~/.pgrx, either pgxn install pg_durable succeeds outright, or it fails
with an error naming the exact command to run.
make pgrx-init and make install-pgrx exist and are listed in make help.
README.md documents the PGXN install path including prerequisites and the
expected build time, and any link in that section works when rendered on
pgxn.org.
USER_GUIDE.md Prerequisites cross-references it.
META.json.in carries a no_index map and pgxn validate-meta passes.
- After the next release the indexed set contains no
prompts/, .github/,
.agents/ or docs/website/ entry, no requirements entry, and README and
USER_GUIDE remain indexed and render.
- The release archive and the GitHub source assets are unchanged in content —
item 3 must not become an export-ignore change.
Notes
None of this can be retrofitted to 0.2.6; a PGXN release is immutable. It all
applies from the next release onward, and 0.2.6 keeps its 69-document index
and its install gate.
Two unrelated observations found during validation
Recorded here because that is where they surfaced. Neither belongs to this
issue; splitting either out is reasonable.
-
pg_durable.enable_superuser_instances is PGC_POSTMASTER but its error
text does not say so:
ERROR: pg_durable: superuser instances are disabled. current_user "postgres" is a
superuser, but pg_durable.enable_superuser_instances is off.
Set pg_durable.enable_superuser_instances = on to allow this.
Following that instruction with ALTER SYSTEM SET + pg_reload_conf() does
nothing; the server log says cannot be changed without restarting the server. Adding "and restart PostgreSQL" to the hint would close the loop.
-
The background worker resolves its connection through libpq environment
defaults. In a container with PGHOST set, the worker silently connected
to that host instead of the local server:
LOG: pg_durable: background worker connected to PostgreSQL at
postgres://postgres@<PGHOST-from-environment>:5433/postgres
LOG: pg_durable: failed to create management pool (will retry in 5s):
pool timed out while waiting for an open connection
PGHOST is routinely set in Docker Compose and Kubernetes environments, so a
worker that follows it can end up pointed at an entirely different database
with only a log line to say so. Whether that is intended is a design question
I have not investigated.
Evidence gathered 2026-08-24 and 2026-08-25 against the published pg_durable
0.2.6 distribution (sha1 c48e369b0d9ac10a177c41c482afa9854b96a47d), a clean
Debian bookworm install with PostgreSQL 17.11 from PGDG, pgxn/pgxn-api
lib/PGXN/API/Indexer.pm at main, pgxn/pgxn-manager
sql/11-dist_management.sql at main, pgxn/pgxnclient
pgxnclient/commands/install.py at master, the PGXN Meta Spec 1.0.0, and the
live pgcollection, pg_local_cache and jsonschema distributions.
Harden the PGXN distribution
Tracking issue for everything needed to make
pg_durableonPGXN actually work for the people who find
it there. The first release (
0.2.6, 2026-08-24) is live and correct as apackage, but an end-to-end validation on a clean machine found the advertised
install command does not work, the documentation does not cover the PGXN entry
point, and the search index carries our internal development notes.
Three work items, in priority order. Item 1 is a functional blocker; items 2 and
3 are quality.
pgxn install pg_durablefails on a clean machineWhat is already verified working
So this issue is not read as "PGXN is broken". Full end-to-end run on Debian
bookworm, PostgreSQL 17.11 from PGDG, Rust 1.98.0,
cargo-pgrx0.16.1,pgxnclient1.3.2, aftercargo pgrx init:pgxn install pg_durablepg_durable.so, 11,595,832 bytes, release build, in/usr/lib/postgresql/17/lib/default_version = '0.2.6'correctly substitutedpg_durable--0.2.6.sqlplus 7 upgrade pathsCREATE EXTENSION pg_durableextversion0.2.6, 44df.*functionsdf.start('SELECT ''Hello, durable world!''')→ instancee2a73657→completed→{"rows": [{"?column?": "Hello, durable world!"}], "row_count": 1}pgxn uninstall pg_durablepgxn uninstalltwicepg_durable is not installed for /usr/bin/pg_config; nothing to removeThe published archive is also byte-identical in
Makefile,META.json.in,scripts/test-make-install.sh,pg_durable.controlandCargo.tomlto git tagv0.2.6(6799781), and the macOS.dylibfix from #348 shipped in it.1. Blocker:
pgxn install pg_durablefails with$PGRX_HOME does not existOn a clean machine with PostgreSQL 17, Rust and
cargo-pgrxall installed, theadvertised command fails after 16 seconds:
make all→package→cargo pgrx package, which requires$PGRX_HOME(
~/.pgrx). That directory is created bycargo pgrx init, andpgxn installnever runs it. Everything downstream is fine; this is purely a first-run gate.
It matters because PGXN is now an advertised install channel and is the only
source path for anyone not on Debian or Docker. It is the first thing a PGXN
user hits, and the error names an environment variable that appears nowhere in
our documentation.
The requirement is narrower than it looks
Three probes against the published
0.2.6archive withcargo-pgrx0.16.1:$PGRX_HOMEstategmake PG_CONFIG=... all$PGRX_HOME does not existconfig.toml)/root/.pgrx/config.toml not found. Have you run `cargo pgrx init` yet?config.tomlpresent listing onlypg18 = "/nonexistent/pg_config"So the requirement is precisely that
$PGRX_HOME/config.tomlexists. Itscontents are irrelevant when
--pg-configis passed explicitly, which ourpackagetarget always does. Two consequences:An existing pgrx configuration pointing at a different PostgreSQL cannot
hijack the build. That was a plausible failure mode; it does not occur.
cargo pgrx init --pg<major> <pg_config>is instant when handed anexisting
pg_config— it validates and writesconfig.toml, and does notdownload or build PostgreSQL:
Proposed fix
Follow the ecosystem precedent.
theory/pg-jsonschema-boon— a pgrx extension published on PGXN by PGXN's own author, and the distribution
whose metadata shape we already copied — ships explicit make targets and
documents them, rather than auto-initializing:
Its
packagetarget is a barecargo pgrx package --pg-configwith no initguard, so by source reading it has the same
$PGRX_HOMEdependency we do.Recommended, in order:
Add
pgrx-initandinstall-pgrxtargets to ourMakefile. We alreadycompute
PG_MAJORandPG_CONFIG, andCargo.tomlpinspgrx = "=0.16.1",so both targets are a few lines. This gives the documentation something
concrete to name instead of a raw
cargo pgrx initincantation.Make the failure actionable. When
$PGRX_HOME/config.tomlis absent,fail before invoking cargo with the exact command to run:
Optionally, guarded auto-init in
package. Runningcargo pgrx init --pg$(PG_MAJOR) "$(PG_CONFIG)"whenconfig.tomlis absentwould make
pgxn install pg_durablework with no user action at all. Theusual objection — that
cargo pgrx initcan download and compile its ownPostgreSQL — does not apply, because we always pass an explicit
pg_config.It only fires when
config.tomlis missing, so it cannot clobber an existingsetup. This diverges from the reference distribution, so it is a judgement
call: strictly friendlier, slightly more magic.
Note that
pgxnclientpassesPG_CONFIGexplicitly on bothmake allandmake install(['gmake', 'PG_CONFIG=/usr/bin/pg_config', 'all']), so there isno risk of the unprivileged build and the
sudoinstall resolving differentPostgreSQL installations.
2. Document the PGXN install path
PGXN carries the source distribution: the published archive is 970 KB, 337
files, and contains zero
.so/.dylib/.dll/.a/.o. Installing meanscompiling a Rust extension on the user's machine, which needs the Rust
toolchain,
cargo-pgrx0.16.1, PostgreSQL development headers, and severalminutes. None of that is currently spelled out for someone arriving via
pgxn install.Today the entire PGXN treatment is one sentence at
README.md:162, at the endof the Packages section, saying PGXN carries the source distribution "built and
installed exactly as described above". It never shows the
pgxncommand, and"above" is the tarball recipe, which is a different entry point.
Where it goes
README.md, Packages section — primary. PGXN rendersREADME.mdas thedistribution landing page:
pgxn.org/dist/pg_durable/contains anh3: READMEfollowed by the entire README. A PGXN visitor is already reading it.
Replace line 162 with a short "Installing from PGXN" block covering
prerequisites,
make pgrx-init(per item 1),pgxn install pg_durable,pgxn uninstall pg_durable, and a pointer to the Docker image or Debianpackages for anyone who wants a prebuilt binary instead.
USER_GUIDE.md, Getting Started → Prerequisites — cross-reference.USER_GUIDE.mdis theprovides.docfileand PGXN links it as "Documentation".Its Prerequisites currently begin after installation —
shared_preload_libraries, restart,CREATE EXTENSION— so there is a gapwhere "how the files got there" belongs. One line pointing at the README is
enough; the install content should not be duplicated.
A separate
INSTALL.mdis not recommended: it splits install guidance acrossthree files and is not a PGXN convention.
Relative links in the README break on PGXN
Whatever we write has to survive being rendered on pgxn.org, and today's README
links do not. Verified 404:
https://pgxn.org/dist/pg_durable/USER_GUIDE.mdhttps://pgxn.org/dist/pg_durable/docs/pg_durable_mvp.mdThe PGXN-rendered copy lives at
/dist/pg_durable/0.2.6/USER_GUIDE.html(HTTP200), so the content is reachable — the link is what is broken. Affected
relative links in
README.md:USER_GUIDE.md,USER_GUIDE.md#privilege-grants,docs/pg_durable_mvp.md,SECURITY.md,docs/TESTING.md#2-pg_regress-tests,examples/README.md,tests/e2e/,.github/workflows/ci.yml.Any link intended to work for a PGXN reader needs an absolute URL. Whether to
convert them all, or only the ones in the install path, is a judgement call —
relative links are correct on GitHub, which is the larger audience.
3. Narrow the indexed document set with
no_indexThe
0.2.6release indexed 69 documents. PGXN indexes documentation forfull-text search, so the distribution page now advertises, alongside the User
Guide:
prompts/pg_durable-releaseprompts/pg_durable-merge-main.github/copilot-instructions.agents/skills/pg-durable-sql/SKILLdocs/dep_issuesdocs/http_problemsdocs/loop_rework_problemsdocs/DUROXIDE_PG_DEADLOCK_ISSUEdocs/CODESPACES_PREBUILDSTODOexamples/*/function-app/requirementsNothing here is secret — the repository is public. The problem is signal: a
reader searching PGXN gets our internal AI development prompts and a list of our
open bugs presented as product documentation, and two
requirements.txtpipmanifests are indexed as "documents" because
Text::Markuptreats.txtasmarkup. All seven files under
prompts/are indexed, including the releaserunbook describing our own PGXN publishing procedure.
The fix:
no_indexinMETA.json.inThe PGXN Meta Spec has a field for exactly this. From
the spec, section
no_index:It is not merely aspirational. Implementation,
pgxn/pgxn-apilib/PGXN/API/Indexer.pm,find_docs()(lines 473-501):Confirmed working in production.
pgcollectionpublishesno_index: {"directory": [".github"], "file": ["AGENTS.md", "CODE_OF_CONDUCT.md", "CONTRIBUTING.md", "GOVERNANCE.md", "SECURITY.md"]}, and all five files areabsent from its indexed
docswhileREADMEanddoc/*remain. Roughly adozen other current distributions use it, including
pg_trickle,provsql,pg_local_cacheandpg_clickhouse.Why not
.gitattributes export-ignoreIt would work —
make pgxn-zipusesgit archive, which honoursexport-ignore— but it is the wrong instrument:source distribution to read or build gets a tree that silently differs from
the repository.
package-release.yml:269(
build-source) buildspg_durable-X.Y.Z.tar.gzand.tar.bz2withgit archivefrom the same tree, so one.gitattributesline silentlychanges three published artifacts at once. GitHub's auto-generated "Source
code" archives honour it too.
them not indexed as product documentation. That is precisely the
distinction
no_indexdraws.no_indexis one field in a file we already generate, affects only indexing andsearch, and leaves every artifact byte-identical.
Two implementation traps
1.
fileentries are matched by exact string equality, with the extension.The API's
docskeys are extension-stripped, so copying them straight out ofhttps://api.pgxn.org/dist/pg_durable.jsondoes not work:docskeyno_index.fileentry requireddocs/dep_issuesdocs/dep_issues.mdTODOTODO.mdPaths are relative to the archive root — the
pg_durable-0.2.6/prefix isstripped before matching, so write
prompts/README.md, notpg_durable-0.2.6/prompts/README.md.2.
directoryentries are an anchored literal prefix match (/^\Q$_/), nota path-component match.
"docs"would also match a hypotheticaldocs2/.Include the trailing slash:
"docs/website/".One useful safety property:
provides.<ext>.docfileis collected before theskip checks run, so
USER_GUIDE.mdcan never be excluded by accident.Proposed change
Add to
META.json.in:That takes the indexed set from 69 to about 50, removing every entry that is
unambiguously internal: AI prompts and skills, CI instructions, the website
source, the repo TODO, three open-problem notes, the Codespaces config, and two
pip manifests.
Deliberately left indexed:
README,USER_GUIDE,CHANGELOG,LICENSE,SECURITY,CONTRIBUTING,CODE_OF_CONDUCT, thedocs/spec-*anddocs/ARCHITECTURE/docs/api-reference/docs/grammarset, and all 13examples/documents.Worth a decision, not obviously either way:
docs/security-review/(3 documents). Publishing a Microsoft OSS securityreview is arguably a credibility asset;
workbook-datais raw table datarather than prose and probably should not be indexed regardless.
parameterized_queries_proposal(explicitlytitled "Status: Not Implemented"),
proposal-management-api,nested-graph-design,named-results-v2,move-duroxide-schema,design-azure-functions. Useful to a contributor, potentially misleading to auser searching for features that do not exist.
TESTING,E2E_TESTING,spec-pg-regress,upgrade-testing.docs/pg_durable_mvp("pg_durable MVP User Guide") appears superseded byUSER_GUIDE. Two user guides in one search index is worse than one.Whatever item 2 adds to the README must not be excluded here.
Verification
Items 1 and 2 are testable before release, on a machine with no
~/.pgrx:Item 3 applies at index time, not bundle time, so
make pgxn-zipoutput isunchanged and there is nothing to check locally beyond metadata validity:
The real check for item 3 happens after the next release lands:
Suggested CI coverage
No workflow currently invokes
pgxnat all.Source Install Checksruns theoffline
scripts/test-make-install.shcontract tests, andmacOS Source Install (PG17)callsmake package/make installdirectlywith an explicit
PG_CONFIG=. Neither would have caught item 1, because bothrunners already have
~/.pgrx.A job running the four commands above on a clean runner would. Because it
exercises the published distribution it can only run after a release, so a
scheduled or manually dispatched job is more appropriate than a per-PR gate.
Acceptance criteria
cargo-pgrxbut no~/.pgrx, eitherpgxn install pg_durablesucceeds outright, or it failswith an error naming the exact command to run.
make pgrx-initandmake install-pgrxexist and are listed inmake help.README.mddocuments the PGXN install path including prerequisites and theexpected build time, and any link in that section works when rendered on
pgxn.org.
USER_GUIDE.mdPrerequisites cross-references it.META.json.incarries ano_indexmap andpgxn validate-metapasses.prompts/,.github/,.agents/ordocs/website/entry, norequirementsentry, andREADMEandUSER_GUIDEremain indexed and render.item 3 must not become an
export-ignorechange.Notes
None of this can be retrofitted to
0.2.6; a PGXN release is immutable. It allapplies from the next release onward, and
0.2.6keeps its 69-document indexand its install gate.
Two unrelated observations found during validation
Recorded here because that is where they surfaced. Neither belongs to this
issue; splitting either out is reasonable.
pg_durable.enable_superuser_instancesisPGC_POSTMASTERbut its errortext does not say so:
Following that instruction with
ALTER SYSTEM SET+pg_reload_conf()doesnothing; the server log says
cannot be changed without restarting the server. Adding "and restart PostgreSQL" to the hint would close the loop.The background worker resolves its connection through libpq environment
defaults. In a container with
PGHOSTset, the worker silently connectedto that host instead of the local server:
PGHOSTis routinely set in Docker Compose and Kubernetes environments, so aworker that follows it can end up pointed at an entirely different database
with only a log line to say so. Whether that is intended is a design question
I have not investigated.
Evidence gathered 2026-08-24 and 2026-08-25 against the published
pg_durable0.2.6 distribution (sha1
c48e369b0d9ac10a177c41c482afa9854b96a47d), a cleanDebian bookworm install with PostgreSQL 17.11 from PGDG,
pgxn/pgxn-apilib/PGXN/API/Indexer.pmatmain,pgxn/pgxn-managersql/11-dist_management.sqlatmain,pgxn/pgxnclientpgxnclient/commands/install.pyatmaster, the PGXN Meta Spec 1.0.0, and thelive
pgcollection,pg_local_cacheandjsonschemadistributions.