Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
138 commits
Select commit Hold shift + click to select a range
e406a00
T071: 13.2 and 13.3 — load_settings refuses a non-PostgreSQL DSN and …
brettheap Sep 28, 2026
91f7973
Fix round: _refuse_non_postgresql_dsn never raises a bare ValueError …
brettheap Sep 28, 2026
b5296f9
Rework #60 to Brett's ruling: OPENDOX_MIGRATION_DATABASE_URL required…
brettheap Sep 28, 2026
f097fd8
Fix round: load_migration_settings gets the same dialect gate load_se…
brettheap Sep 28, 2026
b50e3b1
T070: 13.4, 13.5 and 13.6 — OPENDOX_INSTALL_MODE and generate-and-ope…
brettheap Sep 30, 2026
2c96dfb
T072: 13.1 — the bundled PostgreSQL server, the local install's own c…
brettheap Sep 30, 2026
c3a70a2
T072: extend the dependency lock for the `local` extra and the test e…
brettheap Sep 30, 2026
32683e8
Fix round: the install-mode fixture's repository ignores the user's g…
brettheap Sep 30, 2026
95fe16f
Merge T070's fix round (32683e8) into T072
brettheap Sep 30, 2026
525f61c
Fix round: runtime migrate and reset refuse what a local install cann…
brettheap Sep 30, 2026
32db5d8
Merge T070's second fix round (525f61c) into T072
brettheap Sep 30, 2026
02dadc5
Fix round: status reports a local install's broker on its early retur…
brettheap Sep 30, 2026
ac61596
Merge T070's third fix round (02dadc5) into T072
brettheap Sep 30, 2026
859b37b
Fix round: a healthy local status is proven to exit 0, and RuntimeSet…
brettheap Sep 30, 2026
5e52872
Fix round: the local install's migrations, pid, initdb, start and int…
brettheap Sep 30, 2026
28bdccd
Merge T070's fourth fix round (859b37b6) into T072
brettheap Sep 30, 2026
96b2699
Fix round: an unprovable pid is not believed, and the state dir refus…
brettheap Sep 30, 2026
026f00e
Fix round: the install-mode module says which of its cases is DB-back…
brettheap Sep 30, 2026
4aed627
Merge T070's fifth fix round (026f00ea) into T072
brettheap Sep 30, 2026
a0fb7c8
Fix round: pyproject's packaging note names the resolver that exists …
brettheap Sep 30, 2026
0f77d5c
Fix round: libpq's environment, the socket's path and a relative HOME…
brettheap Sep 30, 2026
379fbb1
Fix round: the socket's path trusts no group and no link it cannot vo…
brettheap Sep 30, 2026
84a6c04
T072: pixeltable-pgserver carries the server, and it authenticates by…
brettheap Sep 30, 2026
f8e6e9e
Fix round: the data path, fresh directories and readiness belong to t…
brettheap Sep 30, 2026
adeb6fe
Merge main (047bb4fa) into T071: phase 2 has landed
brettheap Oct 2, 2026
3185e7d
Merge T071's merge-from-main head (adeb6fed) into T070
brettheap Oct 2, 2026
c8fac05
T070, owed at the merge round: the standalone generate-and-open runs …
brettheap Oct 2, 2026
2210739
T084: the rejection report prints every broken rule once, with its count
brettheap Oct 2, 2026
fe232fe
Merge T070's merge round (c8fac05e) into T072: phase 2 is on the base
brettheap Oct 2, 2026
19e32f0
T072, owed at the merge round: the real entry point, and every --loca…
brettheap Oct 2, 2026
e7324d4
T084: gate and refresh are true only where a contributed route answer…
brettheap Oct 2, 2026
c39d960
Fix round: a PostgreSQL scheme libpq would not read as a URI is refus…
brettheap Oct 2, 2026
9ae5e72
Merge T071's fix round (c39d960e) into T070
brettheap Oct 2, 2026
379ff1f
T073: 13.4a, /capabilities gains an install block, from the serving p…
brettheap Oct 2, 2026
cdf7382
Fix round: the --local callers inherit none of the runner's runtime s…
brettheap Oct 2, 2026
c664775
Merge T073 (openDox-code#72, 379ff1f4) into T084: serve.py's single-w…
brettheap Oct 2, 2026
94254b1
Merge #67's fix rounds (cdf7382b) into T072
brettheap Oct 2, 2026
6eb0bbd
T072: the env probe expects the child's own state directory, never th…
brettheap Oct 2, 2026
d1de1fd
Fix round: the healthy-local status case sets its schema with make_co…
brettheap Oct 2, 2026
7714751
T084: the consumer columns' seams, with openDox's defaults registered…
brettheap Oct 2, 2026
47a6574
Merge T072's merge-round fixes (openDox-code#69, 6eb0bbdb) into T073
brettheap Oct 2, 2026
0488f5b
Fix round: nothing is made through a path the tree check would refuse…
brettheap Oct 2, 2026
20032d0
T073: the hosted case hands its child a hosted install's settings on …
brettheap Oct 2, 2026
a39e020
Merge T073 (openDox-code#72, 20032d02) into T084
brettheap Oct 2, 2026
fedfa75
Merge #67's fix round d1de1fd9 into T072
brettheap Oct 2, 2026
105f2f1
Fix round: no runtime setting the runner exports reaches a tests_runt…
brettheap Oct 2, 2026
1b0c3a6
Merge T072's fix rounds (openDox-code#69, fedfa75d) into T073
brettheap Oct 2, 2026
3426c75
Fix round 12: the auth files are exactly 0600, and the cluster runs o…
brettheap Oct 2, 2026
f66e5f8
Merge #67's fix round 105f2f12 into T072
brettheap Oct 2, 2026
5d4e433
T084: route the last deferred reaches through the column seams (4.3, …
brettheap Oct 2, 2026
a5ace91
Merge T073 (openDox-code#72, 1b0c3a63) into T084
brettheap Oct 2, 2026
65551e6
T084: a tile's own documents are editable in openDox's default scope
brettheap Oct 2, 2026
1fab55f
T084: pin the static bundle's content types ahead of the platform's t…
brettheap Oct 2, 2026
a985407
T072: the in-process local cases give themselves a short state directory
brettheap Oct 2, 2026
55f6e8a
T084: retire consumer_reach; the gate and projection columns are a ho…
brettheap Oct 2, 2026
0b0c37d
T084: the rail's thread read and a standalone own-document turn, held
brettheap Oct 2, 2026
bb05e3d
Merge T072's fix rounds (openDox-code#69, f66e5f82) into T073
brettheap Oct 2, 2026
e214477
Adversarial review M1: a comma in the state directory is refused
brettheap Oct 2, 2026
897029d
Merge T073 (openDox-code#72, bb05e3d7) into T084
brettheap Oct 2, 2026
c4f5df3
Adversarial review L1: the carrier is pinned below 0.7, and another m…
brettheap Oct 2, 2026
b2d80e9
Adversarial review L3: the directory creation starts from is judged b…
brettheap Oct 2, 2026
9e2f303
Adversarial review L2: the local verbs judge their socket before conn…
brettheap Oct 2, 2026
21bde1a
Adversarial review note: no replication connection, logical or physical
brettheap Oct 2, 2026
b1db196
T084 fix round 1: a seam registration with the names but not their sh…
brettheap Oct 2, 2026
bb71ee2
Merge T072's adversarial-review rounds (openDox-code#69, 21bde1af) in…
brettheap Oct 2, 2026
28e195b
Fix round 13: the server is found as the installed distribution's own…
brettheap Oct 2, 2026
94dc15e
Merge T073 (openDox-code#72, bb71ee25) into T084
brettheap Oct 2, 2026
dd015d1
T084: `opendox --help` names the installed command and openDox only
brettheap Oct 2, 2026
539a358
Merge T072's fix round 13 (openDox-code#69, 28e195b9) into T073
brettheap Oct 2, 2026
3da9353
Merge main (8a98e317) into T073
brettheap Oct 2, 2026
bf9bcb0
Fix round 14: a named platform gate, resolution failures as reasons, …
brettheap Oct 3, 2026
57b7ed8
Merge main 66ff7257 into T072: #67 (T070) and #61 (T078) landed
brettheap Oct 3, 2026
3387293
Merge T073 (openDox-code#72, 3da93530) into T084, and main through it
brettheap Oct 3, 2026
058d96e
Fix round 15: on Linux the parent-death signal is armed, or the serve…
brettheap Oct 3, 2026
085ba0b
Merge main 2fc714d2 into T072: #62 (T079) landed
brettheap Oct 3, 2026
ff70ac1
T084 fix round 2: a gate verb on the default gate is refused, not a t…
brettheap Oct 3, 2026
b4e5485
Merge T072 (openDox-code#69, 058d96ef) into T073: stack A carries mai…
brettheap Oct 3, 2026
3ebccb3
Fix round 16: a local install's served role and database are the bund…
brettheap Oct 3, 2026
7cd2370
Merge T073 (openDox-code#72, b4e54857) into T084: stack A's main 66ff…
brettheap Oct 3, 2026
52a2b8d
Merge main 9a490405 into T072: #74 (T081) landed
brettheap Oct 3, 2026
00ff3e8
Merge T072 (openDox-code#69, 085ba0b0) into T073: stack A carries mai…
brettheap Oct 3, 2026
56aae21
T103: every loopback route checks the Host (DNS rebinding) (plan 034)
brettheap Oct 3, 2026
1d6a4f1
Merge T073 (openDox-code#72, 00ff3e83) into T084: stack A's main 2fc7…
brettheap Oct 3, 2026
5a3b51e
T084: openDox's own settings documents are never a tile's editable ma…
brettheap Oct 3, 2026
3e2d5ac
T104: the console token travels in the opened URL, not /capabilities …
brettheap Oct 3, 2026
278855d
T084: an unknown tile kind is a malformed request, never a dropped co…
brettheap Oct 3, 2026
badf247
Merge T084 (openDox-code#77, 1d6a4f19) into T103
brettheap Oct 3, 2026
5a9cb0d
T084: generate-and-open removes the run directory it minted, named op…
brettheap Oct 3, 2026
6519660
T084: `python -m opendox.serve --help` names openDox only, as `opendo…
brettheap Oct 3, 2026
efdc18b
Merge T103 (openDox-code#80, 56aae213) into T104
brettheap Oct 3, 2026
4a9dbe9
Fix round 17: the /proc falsifier is gated, and a backslash in the ma…
brettheap Oct 3, 2026
ff321f3
T104: T103's real local serve reads its token from the private copy
brettheap Oct 3, 2026
620bee2
Merge T072 (openDox-code#69, 52a2b8dc) into T073: stack A carries mai…
brettheap Oct 3, 2026
978f264
T103 fix round 1: an IPv6 loopback bind binds, and its URL is bracket…
brettheap Oct 3, 2026
b333bf1
Merge T073 (openDox-code#72, 620bee27) into T084: T081 arrives throug…
brettheap Oct 3, 2026
8986158
Fix round 18a: both bundle DSNs name their schema, public (Copilot re…
brettheap Oct 3, 2026
c04690f
Fix round 18b: a live pid /proc will not describe fails closed (Copil…
brettheap Oct 3, 2026
fb0393d
Merge T084 (openDox-code#77, b333bf16) into T103
brettheap Oct 3, 2026
7d69dc3
T104: the private copy refuses a state directory in a served root
brettheap Oct 3, 2026
ebe0a35
T084 fix round 3: a group's edges name documents by id, and the scope…
brettheap Oct 3, 2026
7702004
T103 fix round 2: an IPv6 wildcard bind is announced at ::1 (Copilot …
brettheap Oct 3, 2026
d0f1efc
Merge T084 (openDox-code#77, ebe0a35f) into T103
brettheap Oct 3, 2026
bc85f21
Merge T072 (openDox-code#69, c04690f8) into T073: stack A's final pre…
brettheap Oct 3, 2026
6894268
Merge main (5e7ab003) into T073: T072 (#69) landed as a squash
brettheap Oct 3, 2026
93f77dc
T073: the install report reads the bundled server's process once; the…
brettheap Oct 3, 2026
6db50b0
Merge T103 (openDox-code#80, d0f1efcb) into T104
brettheap Oct 3, 2026
7536bfe
Merge T073 (openDox-code#72, 93f77dc1) into T084: #72's head with its…
brettheap Oct 3, 2026
d556c3f
Merge main (90ac7033) into T084: T073 (#72) landed as a squash
brettheap Oct 3, 2026
cb89954
T104 fix round 1: every served root, an exact removal, and a plain ki…
brettheap Oct 3, 2026
1fb81cb
T084 fix round 4: a selection's files are paths and a group's edges a…
brettheap Oct 3, 2026
213344a
T084 fix round 5: an in-root alias of a settings document is the sett…
brettheap Oct 3, 2026
c979747
T104 fix round 2: the copy is judged by its own path, and no static l…
brettheap Oct 3, 2026
514d9ad
Merge T084 (openDox-code#77, 213344ad) into T103: #77's final pre-squ…
brettheap Oct 3, 2026
b9025b6
Merge main (e49b17c3) into T103: T084 (#77) landed as a squash
brettheap Oct 3, 2026
36ff610
T104: the state directory and every served root overlap in neither di…
brettheap Oct 3, 2026
c1e7d8d
Merge T103 (openDox-code#80, b9025b6c) into T104: #80's final pre-squ…
brettheap Oct 3, 2026
60bace0
Merge main (390e2c28) into T104: T103 (#80) landed as a squash
brettheap Oct 3, 2026
219d7cf
T104 fix round 3: a directory's index page is judged, and a FIFO neve…
brettheap Oct 3, 2026
d4b9943
Merge main (0116293a) into T104: T102 (#81) landed as a squash
brettheap Oct 3, 2026
a13857a
T104: every clause of #1144 12.4a as batch N amends it, and the opene…
brettheap Oct 3, 2026
182cac7
Merge main (c4b55cc4) into T104: the T102 follow-on (#85) landed as a…
brettheap Oct 3, 2026
14285fb
T104: the state directory is walked once, every link and directory on…
brettheap Oct 3, 2026
ddb26c3
Merge main (ca9e1bd5) into T104: T082 (#76) landed as a squash; F2 ap…
brettheap Oct 4, 2026
9f32889
T104 fix round 5: the snapshot files are served roots, publication an…
brettheap Oct 4, 2026
9dcc9bb
T104 fix round 6: the walk's operating-system errors are refusals by …
brettheap Oct 4, 2026
fb8a1cc
T104 fix round 7: Ctrl-C is held like SIGTERM while a copy is written…
brettheap Oct 4, 2026
66cffdb
T104 fix round 8: a running console's copy is reserved, and every ung…
brettheap Oct 4, 2026
9fe57df
T104: a snapshot named at a copy not yet written refuses the start; t…
brettheap Oct 4, 2026
021944a
Merge main 38d3350e (T100, #82) into T104
brettheap Oct 4, 2026
45958bf
T104 fix round 9: every standalone plane keeps the boundary; identity…
brettheap Oct 4, 2026
c4e6c01
T104: the start prints the ruled hint line for a browser that cannot …
brettheap Oct 4, 2026
0539f8c
T104: a removal releases the copy's reservation, and a copy's repr ne…
brettheap Oct 4, 2026
af2a2ef
T104 fix round 10: the tokenless guard walks the state directory once…
brettheap Oct 4, 2026
ed6e476
T104 fix round 11: a copy is known by what it holds, a check that can…
brettheap Oct 4, 2026
d466c1d
T104: no test server outlives its case or its run
brettheap Oct 4, 2026
1e114a1
T104: a partly written copy is refused by its place
brettheap Oct 4, 2026
a2e3665
T104 fix round 12: a copy is written from a marker, and what is sent …
brettheap Oct 4, 2026
c5fcdfa
T104 fix round 13: the console page's URL is judged as a browser read…
brettheap Oct 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 83 additions & 27 deletions src/opendox/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@
# the driver is imported when the server is started, never here.
from opendox.runtime import bundle as bundle_mod # noqa: E402
from opendox.runtime import migrations as migrations_mod # noqa: E402
# THE CONSOLE TOKEN'S PRIVATE COPY (plan 034 T104): on a standalone plane the
# token is not on `/capabilities`, and `generate-and-open` opens the page
# through a 0600 copy in the state directory instead. Stdlib-only.
from opendox import console_access # noqa: E402
from opendox.boundary import ( # noqa: E402
BoundaryViolation, HumanGate, OutputBoundary,
)
Expand Down Expand Up @@ -893,34 +897,86 @@ def _generate_and_serve(args: argparse.Namespace, run_dir: Path, *,
# bundled server it started as its child.
install_report=_install_report(args))
url = serve_mod.server_url(httpd, "/index.html")
print(f" serving {url}")
print(f" snapshot {serve_mod.server_url(httpd, '/snapshot.json')}")
# The URL is ALWAYS printed on its own line, AND FLUSHED (plan 034 T056).
# Where standard output is a pipe or a file, Python buffers it by block,
# and the process is about to block in `serve_forever()`. So without the
# flush, a wrapper reading this line never sees it while the server runs,
# and it cannot learn an ephemeral port or tell that the server started.
# Measured at openDox-code#59 e3ef506a: zero lines in 20 s on a pipe.
print(url, flush=True)

if not args.no_open:
# THE CONSOLE TOKEN, ON A STANDALONE PLANE (plan 034 T104; RULED
# openxFactory#656 `5963851934`). `/capabilities` no longer carries it, so
# this entry point writes it into a 0600 private copy in the install's
# state directory, an HTML page that forwards to `url` with the token in
# the FRAGMENT. The browser is handed the copy's PATH, because a URL given
# to `webbrowser.open` sits on a command line every user can read
# (`/proc/<pid>/cmdline`). The path is printed with or without
# `--no-open`, and the token never is: opening that file again re-opens
# the page. `None` on a host's plane, where no token was minted (a
# standalone plane still keeps every console's copy unserved then,
# `console_access.guard_private_roots`), and under `--no-serve`. A copy
# that cannot be written safely refuses the run before it serves.
#
# A plain `kill`, or a closed terminal, stops a standalone console the way
# Ctrl-C does (`terminate_as_interrupt`), from BEFORE the copy is written
# to after it is removed (Copilot at openDox-code#84, r4175213864), and a
# stop that arrives while the copy is being written or removed is held
# until that is done (`deferred_termination`), so no copy is ever left
# half handled. A plane that writes no copy keeps the signals' defaults.
#
# `--no-serve` SERVES NOTHING, SO IT PUBLISHES NOTHING (adversarial review
# of openDox-code#84, B8). It closes the server as soon as it has printed
# the URL, so a copy written for it opened a console page nothing
# answered, and was deleted as the run returned. No copy is written, none
# is opened, and no console line is printed.
console = None
serving = not args.no_serve
with console_access.terminate_as_interrupt(
serving and console_access.needs_copy(httpd)):
try:
opener(url)
except Exception as exc: # a headless box has no browser — never fatal
print(f" (could not open a browser: {exc}; open the URL above manually)")

if args.no_serve:
httpd.server_close()
return 0

print(" serving until interrupted (Ctrl-C to stop)", flush=True)
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
finally:
httpd.server_close()
return 0
try:
with console_access.deferred_termination():
if serving:
console = console_access.publish(httpd, page_url=url)
print(f" serving {url}")
print(f" snapshot {serve_mod.server_url(httpd, '/snapshot.json')}")
if console is not None:
print(f" console {console.file_url} (this user's private "
"copy, mode 0600: open it to open the console page "
"again)")
# A browser that cannot open it is told the way past it,
# in one line with no token (RULED, B3).
print(f" {console_access.UNOPENABLE_HINT}")
# The URL is ALWAYS printed on its own line, AND FLUSHED (plan
# 034 T056). Where standard output is a pipe or a file, Python
# buffers it by block, and the process is about to block in
# `serve_forever()`. So without the flush, a wrapper reading
# this line never sees it while the server runs, and it cannot
# learn an ephemeral port or tell that the server started.
# Measured at openDox-code#59 e3ef506a: zero lines in 20 s on
# a pipe. It carries no token.
print(url, flush=True)

if not args.no_open:
try:
opener(console.file_url if console is not None else url)
except Exception as exc: # a headless box has no browser — never fatal
print(f" (could not open a browser: {exc}; open the "
f"{'console file' if console is not None else 'URL'} "
"above manually)")

if args.no_serve:
return 0

print(" serving until interrupted (Ctrl-C to stop)", flush=True)
httpd.serve_forever()
except console_access.ConsoleAccessRefused as exc:
print(f"generate-and-open refused: {exc}", file=sys.stderr)
return 1
except KeyboardInterrupt:
pass
return 0
finally:
# The copy goes with the server: its token is this serve's, and
# dies with it. It goes FIRST, while this process still holds the
# port, so no later serve can bind it and write its own copy in
# between. A stop that arrives meanwhile lets it finish.
with console_access.deferred_termination(raise_pending=False):
console_access.remove_private_copy(console)
httpd.server_close()


# ---- gate console (US9): human-only executable gate actions ----------------
Expand Down
Loading
Loading