Commit Graph

636 Commits

Author SHA1 Message Date
Jakub Zych
e6777bda97 fix(11-08): record T-11-31 and T-11-32 in the security review
- check-phase11.sh --evidence refused the 11-08 threats missing from the
  review; adds both rows, the RC-14 removal check and the CR-01 fix row
2026-09-30 21:00:20 +02:00
Jakub Zych
d4fc957f8f fix(11-08): add the T-11-31 removal check to the phase gate
- RC-14 removes the foreign-transaction refusal in lagoon.AfterCommit and
  requires TestTransactionAfterCommit to fail
2026-09-30 21:00:20 +02:00
Jakub Zych
8e0083ed41 fix(11-08): document foreign and nested transaction refusal
- lagoon.Transaction doc and README state that a nested call over a root
  handle returns an error instead of opening an independent transaction
- beachcomber README no longer promises an immediate sync inside a plain
  GORM transaction; it is warned and skipped since 11-08
2026-09-30 21:00:20 +02:00
Jakub Zych
2766f34d99 test(11-08): keep sync failure coverage managed 2026-09-30 20:49:31 +02:00
Jakub Zych
6f57604028 docs(11.1): create phase plan
Six plans (tracer generator, site UX and checkers, content A, content B,
acme/blog walkthrough, unit tests). SC4/DOCS-04 narrowed to docs/ pages per
D-18; README Go fence conversion logged as a todo.
2026-09-30 20:33:51 +02:00
Jakub Zych
a33b1ada80 fix(11-08): refuse unmanaged after-commit work 2026-09-30 20:14:42 +02:00
Jakub Zych
f7b6b0c313 fix(11-08): make cabana writes commit-safe 2026-09-30 20:14:23 +02:00
Jakub Zych
9033d81721 docs(11.1): record plan-count checkpoint decisions, DOCS requirements and pattern map 2026-09-30 19:39:50 +02:00
Jakub Zych
30d3bfec67 docs(11): create phase gap plan 2026-09-30 16:14:17 +02:00
Jakub Zych
dd97fd12a6 docs(11): add phase verification report (gaps found) 2026-09-30 15:31:09 +02:00
Jakub Zych
b7b48f8771 docs(phase-11): revert premature Complete requirements after gaps found 2026-09-30 15:31:09 +02:00
Jakub Zych
61da4d1a4c fix(11): let the Phase 10 gate accept the Phase 12 pending goldens
- 11-06 records TestBroadcastGoldens/created and /updated and reports them
  as skipped until Phase 12; the Phase 10 detector refused any skip, so
  check-phase10.sh --all failed on the fonoteka.go suite
- mirrors 73cfed7 (check-phase10.1.sh): the detector accepts exactly those
  skips when their output carries 'pending: Phase 12'; the self-test proves
  a pending skip passes and one without the text fails
2026-09-30 15:22:00 +02:00
Jakub Zych
5fa062d554 docs(11): record code review disposition 2026-09-30 15:18:21 +02:00
Jakub Zych
2fb0f7d328 docs(11): add code review report 2026-09-30 15:18:20 +02:00
Jakub Zych
4cc6fd7a42 docs(11-07): record plan 11-07 progress, decisions and requirements 2026-09-30 14:57:33 +02:00
Jakub Zych
45fb00af1a docs(11-07): complete Phase 11 unit tests and gate plan 2026-09-30 14:56:53 +02:00
Jakub Zych
73cfed74ed fix(11-07): let the Phase 10.1 gate accept the Phase 12 pending goldens
- 11-06 records TestBroadcastGoldens/created and /updated and reports them
  as skipped until Phase 12; the 10.1 detector refused any skip, so
  check-phase10.1.sh --all failed on the fonoteka.go suite
- the detector now accepts exactly those skips when their output carries
  'pending: Phase 12' (the same rule check-phase11.sh enforces); the
  self-test proves a pending skip passes and one without the text fails
2026-09-30 14:55:16 +02:00
Jakub Zych
a34c6ece18 docs(11-07): security review with removal checks and the validated test map
- 11-SECURITY-REVIEW.md: T-11-01..T-11-30 and T-11-SC with each plan's
  severity and disposition, mitigation, test and result; RC-01..RC-13
  removal checks for every high mitigated threat; the three defects fixed
  in 11-07
- 11-VALIDATION.md: task ids, plans and waves per row, commands run,
  status validated, nyquist_compliant and wave_0_complete true
2026-09-30 14:49:36 +02:00
Jakub Zych
7743487b76 test(11-07): add the fail-closed Phase 11 gate and removal harness
- scripts/check-phase11.sh: --self-test, --hygiene, --go, --postgres,
  --named, --evidence, --all (prints 'phase11 all passed') and --removal
- the go test -json detector refuses failures, skips, zero tests and
  'no tests to run'; only the two Phase 12 broadcast goldens may skip, and
  only with their pending text
- hygiene refuses application names in the Phase 11 framework files, the
  Centrifugo/Typesense/Web Push client libraries, a direct cron
  requirement, River other than v0.47.0 and a module without README or
  root row; each rule returns on its first violation and the self-test
  proves each refuses its own plant and accepts look-alikes
- --removal: anchor-exact mutations for the high threats, each required to
  fail its named test on an assertion and restored byte for byte (cmp)
2026-09-30 14:42:38 +02:00
Jakub Zych
e55d2345b7 test(11-07): pin that a keyless Typesense engine is never configured
- TestSyncEngineRegistration: the typesense import registers the driver
  and a missing or blank api_key reports Configured false, the gate that
  keeps beachcomber from sending anything
2026-09-30 14:34:33 +02:00
Jakub Zych
11b5b4cdf4 test(11-07): cover the fake Centrifugo recorder edges
- TestCentrifugoRecorder: info/unsubscribe answers, 405 with Allow, 413
  above the body cap (not recorded), authorization as a comparison that is
  never stored, empty key never authorized, loopback ListenAndServe and
  shutdown, waitListening and sleepCtx failures
- TestFlowIDNames: default masked id variables
2026-09-30 14:28:51 +02:00
Jakub Zych
18d3097be5 test(11-07): pin the Typesense engine wire contract
- TestEngineWire: API key and Accept on every request, create on 404 with
  the schema or auto fields, 409 as success, JSONL import with
  success:false and unreadable lines as errors (message capped, no
  document), 404-tolerant delete/flush with id escaping, SearchIDs
  parameters and id parsing, typed errors without bodies, transport
  errors without the URL, timeout
- TestEngineConfig: search.typesense.* parsing and the registered engine
  (coverage 95.6%)
2026-09-30 14:28:02 +02:00
Jakub Zych
6df43d45b8 fix(11-07): roll back the savepoint when a swallowed read failed
- beachcomber and lighthouse released their savepoint whenever the inner
  function reported no error; a Gate that counts a failed read as off,
  or a channel function or delete snapshot that swallows one, left the
  caller's Postgres transaction aborted (25P02) and failed the write
- a failed RELEASE now rolls back to the savepoint, as the READMEs promise
- beachcomber gets its testcontainers harness and sync tests
  (TestSyncGates, TestSyncAfterCommit, TestSyncDeleteAndSoftDelete,
  TestSyncFailuresNonFatal, TestServiceSetup); lighthouse gets
  TestBroadcastSwallowedReadFailure
2026-09-30 14:26:51 +02:00
Jakub Zych
6dadbf6957 test(11-07): cover flare VAPID, allowlist, statuses and config
- TestVAPIDHeader (origin rules, exp, subject), TestVAPIDKeys,
  TestSendAllowlist (T-11-22 host table), TestSendStatuses (2xx, 404/410,
  StatusError without body, disabled, host-only transport errors),
  TestFlareConfig, TestAgo, TestEncryptRejects (coverage 90.0%)
2026-09-30 14:22:44 +02:00
Jakub Zych
33194a1f98 test(11-07): cover lighthouse realtime and the Centrifugo driver
- lighthouse: TestSuppression (Widget silenced, Gadget not, nesting,
  stale outer ctx), TestBulkEmitsOnce, TestBroadcastEdges (zero-key batch,
  update actor, id-only delete, method contract, multi-channel, savepoint),
  TestBroadcastPublishFailure, TestFromSelectsDriver, TestMountSurfaces,
  TestRegistry under -race, drivers, args JSON, Bind (coverage 91.7%)
- centrifugo: TestTokenClaims, TestTokenHandler, TestClientRequests,
  TestClientLoadConfig and a TestProxy table porting the WinterCMS WS-005,
  WS-007 and WS-013 cases (coverage 92.4%)
2026-09-30 14:21:03 +02:00
Jakub Zych
35ac96d664 test(11-07): cover jobs, scheduler and lagoon seams branch by branch
- conga: TestOutcome*, TestCancelQueuedNeverRuns, TestCancelRunningCancelsCtx,
  TestStopJobFromWorker, TestManagerPHPSemantics, principal, delay,
  registration, worker and queue-setting branches; TestQueueClear and
  TestQueueWork move to commands_test.go with the batch/state and
  queue-filter cases (coverage 92.5%)
- scheduler: validation, ordering, missing catalog, log writer, dueAt
- lagoon: TestQueueMigrationsUpDown (River v7 + summer_jobs, rollback,
  idempotent rerun), OnDatabase isolation, Transaction edges
- bonfire TestCallEdges, pact TestCadence
2026-09-30 14:14:39 +02:00
Jakub Zych
6f50b6c940 fix(11-07): enqueue broadcast jobs before GORM commits a single write
- lighthouse:after_create/update/delete also declare
  Before(gorm:commit_or_rollback_transaction); an After-only anchor put
  them past GORM's own commit, so a plain gdb.Create enqueued its
  broadcast job after the commit on the pool (deferred from 11-05)
- lighthouse gets the testcontainers Postgres harness and TestBroadcastTx
  (commit publishes once, rollback nothing, single-statement write
  enqueues on its own transaction, failed write enqueues nothing)
2026-09-30 14:05:17 +02:00
Jakub Zych
c544319cec fix(11-07): hand after-commit callbacks a clean statement
- lagoon.Transaction, the lagoon:after_commit flush and the immediate
  AfterCommit path pass a handle with an empty statement on the write's
  connection (Session NewDB+Context, Clauses(), Session NewDB)
- a WithContext query through the handle no longer continues from the
  written model's statement (deferred from 11-05)
- TestTransactionAfterCommit/callback_handle_has_a_clean_statement covers
  the implicit, plain-transaction and lagoon.Transaction paths
2026-09-30 14:01:22 +02:00
Jakub Zych
6562d94ef3 docs(11-04): record plan 11-04 progress and decisions 2026-09-30 13:54:50 +02:00
Jakub Zych
a0f65cdc09 docs(11-04): complete flare Web Push and websockets commands plan 2026-09-30 13:54:25 +02:00
Jakub Zych
f55cb444ab feat(11-04): add the websockets health, VAPID key and test-push commands
- centrifugo.Client.Info probes the info API method; an error body fails
- websockets:health ports CentrifugoHealthCheck: exits 1 without an API
  key or when the probe fails, prints the Setting/Value table otherwise
- websockets:generate-vapid-keys prints a new P-256 pair, shows configured
  keys only truncated, and --update persists them to overrides.yaml
- websockets:test-push reads subscriptions from an app-published
  SubscriptionSource, refuses to send while push is disabled and sends
  one encrypted push per subscription
- no command prints a configured private key or the Centrifugo API key
- flare and lighthouse READMEs document the CLI commands
2026-09-30 13:52:44 +02:00
Jakub Zych
5fb22c28d0 fix(11-04): keep earlier overrides when compass Persist saves
Persist rewrote overrides.yaml with only this process's runtime values,
so saving one key (for example websockets:generate-vapid-keys --update)
dropped every key persisted earlier. It now starts from the saved file
and lets runtime values win.
2026-09-30 13:45:13 +02:00
Jakub Zych
a9af0d77c7 feat(11-04): add flare Web Push with a stdlib VAPID driver
- RFC 8291 aes128gcm encryption from crypto/ecdh, crypto/hkdf and AES-GCM,
  matching the RFC 8291 Appendix A vector byte for byte
- RFC 8292 vapid t=<ES256 JWT>, k=<key> header (aud origin, exp +12h, sub)
- Pusher, Subscription, SendOptions, SubscriptionSource, Service and From
  reading push.* (enabled, keys, subject, ttl, allowed_hosts)
- sends only to https endpoints on push.allowed_hosts, checked before
  dialing, and never follows redirects; 404/410 map to ErrSubscriptionGone
- module README and root modules row
2026-09-30 13:43:09 +02:00
Jakub Zych
a8305a015d docs(11-06): record plan 11-06 progress and decisions 2026-09-30 13:34:30 +02:00
Jakub Zych
67d1a25e31 docs(11-06): complete Centrifugo broadcast goldens and realtime route parity plan 2026-09-30 13:34:05 +02:00
Jakub Zych
5382947ef8 fix(11-06): send Cache-Control: no-cache, private on the jwt.auth 401
The recorded PHP 401 for a missing bearer (realtime token no-bearer
case) carries Laravel's default Cache-Control header; the Go guard's
401 omitted it, so the replay failed on header.Cache-Control.
2026-09-30 13:31:55 +02:00
Jakub Zych
9ecbf74a22 feat(11-06): add the tide fake Centrifugo recorder, broadcast goldens and parity:broadcasts
- CentrifugoRecorder records publish/broadcast requests (method, path,
  whether the API key matched, JSON body) and binds loopback only
- BroadcastGolden load/write, NormalizePublications (timestamps, actor,
  captured ids only) and DiffPublications (structural, key order ignored)
- RecordBroadcasts runs a flow or one step against a loopback backend
- summer parity:broadcasts wraps it; README documents format and rules
2026-09-30 13:21:23 +02:00
Jakub Zych
fb4aed176a docs(11-05): record plan 11-05 progress and decisions 2026-09-30 13:07:37 +02:00
Jakub Zych
0a1fdb5aa0 docs(11-05): complete beachcomber search sync plan 2026-09-30 13:06:46 +02:00
Jakub Zych
9543e6508c fix(11-05): sync single-statement and plain-transaction writes, type engine status errors
- pin the sync callbacks before gorm:commit_or_rollback_transaction: an
  After-only anchor is appended past the commit and lagoon's after-commit
  flush, so single-statement writes never synced
- give the gate and the document builder a clean session: Session with NewDB
  and a Context clones the write's statement, and a later WithContext queried
  through the written model's table
- typesense.StatusError carries method, path and status, never the body
- README: sync semantics, the three gates, delete on soft delete, and the
  SQL re-gate required of SearchIDs callers
2026-09-30 13:05:10 +02:00
Jakub Zych
3e1f3e6a1b feat(11-05): add the beachcomber search sync package and its Typesense engine
- Searchable, Engine, Gate and an init-time engine registry with the null engine
- GORM callbacks installed through lagoon.OnDatabase register an after-commit
  sync that reloads the row and upserts or deletes its document
- Gates run before any request: engine configured, database published, app Gate
- hand-rolled net/http Typesense engine following the Scout wire contract
- module README and root modules row
2026-09-30 12:50:54 +02:00
Jakub Zych
0d45698ad7 docs(11-03): record plan 11-03 progress and decisions 2026-09-30 12:38:42 +02:00
Jakub Zych
499177a242 docs(11-03): complete lighthouse realtime plan 2026-09-30 12:37:58 +02:00
Jakub Zych
eab2b007f5 docs(11-03): record the websockets plugin dissolution (D-16) and the RT-01 client note
- PROJECT.md: websockets is not a separate app plugin; Key Decisions row
- REQUIREMENTS.md: RT-01 names a hand-rolled Centrifugo client (D-12)
2026-09-30 12:36:16 +02:00
Jakub Zych
211c413273 feat(11-03): broadcast model writes through River jobs enqueued in the write transaction
- Broadcastable contract and Bind[T] bindings; event {action}.{alias},
  default {model, actor, timestamp, ttl} payload, delete snapshot taken
  before the row goes
- GORM callbacks installed via lagoon.OnDatabase enqueue a summer.broadcast
  job on the write's *sql.Tx inside a savepoint; failures are logged and
  never abort the write; zero-key batch writes are skipped
- WithoutBroadcasting[T] (ctx-scoped, per type) and Service.Emit for one
  summary event; the one-attempt job namespaces channels and publishes or
  broadcasts; the payload travels as a JSON string so JSONB keeps its order
- no jobs for the null driver or Centrifugo without an API key
2026-09-30 12:36:07 +02:00
Jakub Zych
79fd705680 feat(11-03): re-authorize every Centrifugo subscribe through a namespace registry
- lighthouse: Registry of namespace authorizers (Result, Allowed, Denied),
  ParseChannel, ChannelID with PHP (int)-cast semantics (PHPInt, pinned by
  a php -r table test), FormatChannels, WithClientID/ClientID
- centrifugo: ProxyHandler (constant-time X-Centrifugo-Secret, HTTP 200
  generic deny, info [] on allow, presence allow/override merge, 64 KiB
  body cap) mounted as the ServerToServer subscribe route
- README: proxy contract, registry and channel rules
2026-09-30 12:29:09 +02:00
Jakub Zych
cada7a4442 feat(11-03): add the lighthouse realtime package and its Centrifugo driver
- lighthouse: Service/From with realtime.driver selection, RegisterDriver
  registry, null/log/memory drivers, Route/Surface/Mount, users and actors
- centrifugo: HTTP API client (apikey header, 2xx success, no request
  without a key), five-generator HS256 TokenIssuer, TokenHandler with the
  WinterCMS 401/503 bodies
- module README and root modules table row
2026-09-30 12:18:11 +02:00
Jakub Zych
f1077382f5 docs(11-02): complete scheduler plan 2026-09-29 22:54:28 +02:00
Jakub Zych
2237a640d2 feat(11-02): add schedule:run as a scheduler process and a cron --once mode
- schedule:run runs a worker on the scheduled queue with every plugin's periodic jobs
- schedule:run --once runs entries due in the current app.timezone minute without River,
  warns and skips unregistered commands, and returns the first command error
- summer schedule:run delegate forwards --once
- tests for --once minute matching, forged-entry skipping (T-11-09) and ByPeriod dedupe
2026-09-29 22:34:21 +02:00
Jakub Zych
d9f939a1ea feat(11-02): run plugin schedules as River periodic jobs through bonfire.Call
- pact.HasSchedule with ScheduledCommand and Daily/DailyAt/Every cadences (no River import)
- bonfire.Call, Catalog and ErrUnknownCommand for in-process command runs
- conga Daily/Every wall-clock schedules in app.timezone, periodic jobs on every worker,
  scheduled queue (MaxAttempts 1, unique by args within the cadence period)
- scheduled worker runs only entries matching the compiled table; unregistered
  commands are skipped with a Warn log
- generated app main publishes bonfire.NewCatalog(commands); hello main regenerated
2026-09-29 19:47:51 +02:00