Skip to content

feat: port project_totp_event to Go listener (#97)#118

Merged
PaulDotterer merged 2 commits into
mainfrom
feat/97-totp-go-projector
May 4, 2026
Merged

feat: port project_totp_event to Go listener (#97)#118
PaulDotterer merged 2 commits into
mainfrom
feat/97-totp-go-projector

Conversation

@PaulDotterer

@PaulDotterer PaulDotterer commented May 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Second projector port under the pattern proven by #96. Replaces project_totp_event (PL/pgSQL, 5 event-type branches + cross-stream effect on users_projection.totp_enabled) with projectors.TotpListener (Go, post-commit listener).

Closes #97.

Coverage

  • TOTPSetupInitiated — upsert totp_projection (resets verified+enabled to FALSE on re-init)
  • TOTPVerified — flip totp_projection.verified+enabled = TRUE AND users_projection.totp_enabled = TRUE
  • TOTPDisabled — drop the totp_projection row AND users_projection.totp_enabled = FALSE
  • TOTPBackupCodeUsed — mark a single 1-based slot in backup_codes_used (event payload uses 0-based index; listener converts)
  • TOTPBackupCodesRegenerated — replace backup_codes_hash and reset backup_codes_used to a fresh all-FALSE array of matching length

Async-vs-sync analysis

Same shape as #96. TotpEnabled is read on the auth/login path (not on the same request that emits a TOTP event), so the post-commit projection write closes the gap well before the next read. The login flow's logout/login cycle dwarfs the listener latency.

Migration shape

project_totp_event is dispatched by the shared project_event() trigger (not its own dedicated trigger like security_alert had). Migration 018 replaces it with a no-op stub — the dispatcher keeps PERFORMing it (no projection_errors noise), Go listener does the actual work. Once the dispatcher itself is rewritten to skip Go-ported streams, the no-op stub can be dropped.

Test plan

  • TestTotpListener_LifecycleEndToEnd — Setup → Verify → BackupCodeUsed → Disable, asserting both projections at each step
  • TestTotpListener_BackupCodesRegenerated — separate coverage of the regenerate branch including length-matching reset
  • TestTotpListener_IgnoresWrongStreamType — defensive: TOTPSetupInitiated under stream_type=user is a no-op
  • go build ./... clean
  • No regressions on existing TOTP handler tests

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor
    • Enhanced TOTP event processing and projection management to ensure consistent and reliable handling of TOTP setup, verification, disabling, and backup code operations across all system configurations.

Second projector ported under the pattern set by #96. Replaces the
PL/pgSQL project_totp_event function with projectors.TotpListener,
covering all five TOTP event types:

  - TOTPSetupInitiated: upsert totp_projection (resets verified +
    enabled to FALSE on re-init)
  - TOTPVerified: flip totp_projection (verified + enabled = TRUE)
    AND cross-stream users_projection.totp_enabled = TRUE
  - TOTPDisabled: drop the totp_projection row AND cross-stream
    users_projection.totp_enabled = FALSE
  - TOTPBackupCodeUsed: mark a single 1-based slot in
    backup_codes_used (event payload uses 0-based index, listener
    converts)
  - TOTPBackupCodesRegenerated: replace backup_codes_hash and reset
    backup_codes_used to a fresh all-FALSE array of matching length

Async-vs-sync analysis matches #96: TotpEnabled is read on the
auth/login path (not on the same request that emits a TOTP event),
so the post-commit projection write closes the gap well before the
next read in practice.

Migration 018 replaces project_totp_event with a no-op stub. The
shared project_event() dispatcher trigger still PERFORMs it for
every totp-stream event; the no-op keeps that quiet (no
projection_errors entries) until the dispatcher itself is rewritten
to skip stream types with Go projectors. Down restores the original
function verbatim.

New sqlc queries: UpsertTotpProjection, VerifyTotpProjection,
DeleteTotpProjection, MarkTotpBackupCodeUsed,
RegenerateTotpBackupCodes, SetUserTotpEnabled. Idempotent shapes
throughout — every UPDATE matches by user_id + projection_version,
the upsert uses ON CONFLICT.

Tests:
  - TestTotpListener_LifecycleEndToEnd walks Setup → Verify →
    BackupCodeUsed → Disable through testcontainers Postgres,
    asserting both totp_projection AND users_projection.totp_enabled
    flips at each step.
  - TestTotpListener_BackupCodesRegenerated covers the regenerate
    branch separately, including verifying that backup_codes_used
    resets to fresh-FALSE-of-new-length.
  - TestTotpListener_IgnoresWrongStreamType pins the stream-type
    guard so a future classifier loosening can't silently start
    inserting totp_projection rows for non-totp events.

Refs #97, tracker #107
@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@PaulDotterer has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 24 minutes and 2 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b9bcf1d9-fc72-4906-bd5b-54884bea3228

📥 Commits

Reviewing files that changed from the base of the PR and between bba83d0 and 60bdbd2.

📒 Files selected for processing (3)
  • cmd/control/main.go
  • internal/projectors/wire.go
  • internal/testutil/testutil.go
📝 Walkthrough

Walkthrough

The pull request ports the PL/pgSQL project_totp_event function to a Go event listener (TotpListener), replacing database-side projection logic with application-side event handling. New sqlc queries support the write operations, a SQL migration no-ops the old function, and the listener is wired into the control server's post-commit event dispatch.

Changes

TOTP Projector Migration

Layer / File(s) Summary
Helper Utilities
internal/projectors/totp.go
Generic deref[T] safely dereferences nullable pointers; totpEventPayload models event JSON fields (secret_encrypted, backup_codes_hash, index); decodeTotpPayload validates and unmarshals TOTP event data, returning ErrIgnoredEvent for non-totp streams.
Event Listener
internal/projectors/totp_listener.go
TotpListener implements store.EventListener, routing decoded TOTP events by type: TOTPSetupInitiated upserts projection; TOTPVerified enables TOTP and updates cross-stream users_projection; TOTPDisabled deletes projection and disables user TOTP; TOTPBackupCodeUsed marks backup code slots; TOTPBackupCodesRegenerated refreshes backup codes. Per-event DB errors are logged without aborting.
Projection Queries
internal/store/queries/totp.sql
New write-side sqlc queries: UpsertTotpProjection, VerifyTotpProjection, DeleteTotpProjection, MarkTotpBackupCodeUsed, RegenerateTotpBackupCodes, and SetUserTotpEnabled for cross-stream user projection updates.
SQL Migration
internal/store/migrations/018_totp_projector_to_go.sql
+goose Up replaces project_totp_event with a no-op stub; +goose Down restores the original PL/pgSQL logic handling all TOTP event types and projection updates.
Server Wiring
cmd/control/main.go
projectors.TotpListener is registered alongside other projectors in the store's post-commit event listener chain with component logging context.
Integration Tests
internal/projectors/totp_test.go
TestTotpListener_LifecycleEndToEnd verifies full TOTP event flow (setup, verify, backup code usage, disable) and projection state via polling; TestTotpListener_BackupCodesRegenerated isolates backup code regeneration; TestTotpListener_IgnoresWrongStreamType confirms stream-type filtering. Includes polling helpers and projection readback struct.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 The TOTP projector hops from SQL to Go,
Events decoded, routes flow swift and true,
Backup codes toggle with precision and care,
From PL/pgSQL dusk to Goland's fair air—
Two-factor hops onward, secure as can be!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: porting the PL/pgSQL project_totp_event function to a Go listener implementation.
Linked Issues check ✅ Passed The PR implements all core objectives from #97: Go projector with Apply logic for all five TOTP event types, registration in cmd/control/main.go, post-commit listener integration, migration 018 replacing PL/pgSQL with no-op stub, comprehensive tests covering lifecycle/backup codes/stream-type filtering, and parity with prior ports (#77, #81).
Out of Scope Changes check ✅ Passed All changes directly support the stated objective of porting project_totp_event to Go: new projector implementation, listener registration, supporting SQL queries, migration, and tests—with no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/97-totp-go-projector

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 24 minutes and 2 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@PaulDotterer

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@PaulDotterer

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 4, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
internal/projectors/totp_test.go (1)

29-175: ⚡ Quick win

Please add a re-init test for the ON CONFLICT path.

The new setup query has important second-write behavior — replace the secret/codes and reset verified/enabled to FALSE — but the suite only exercises first-time setup. A second TOTPSetupInitiated after TOTPVerified is the branch most likely to regress in this port.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/projectors/totp_test.go` around lines 29 - 175, Add a new test to
exercise the ON CONFLICT "re-init" path: after creating a user and running
TestTotpListener_LifecycleEndToEnd’s happy-path events (TOTPSetupInitiated ->
TOTPVerified), append a second TOTPSetupInitiated event with a different
secret_encrypted and different backup_codes_hash, then poll the totp projection
(using pollForTotpRow) and users projection (using pollForUserTotpEnabled) to
assert that the totp row now contains the new secret and new backup_codes_hash,
that Verified and Enabled are reset to false, and that BackupCodesUsed has been
recreated with the new length all set to false; add this as a separate test
(e.g., TestTotpListener_ReinitOnConflict) or extend the lifecycle test to
include this second-setup scenario.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/projectors/totp_listener.go`:
- Around line 70-87: The two related writes (VerifyTotpProjection +
SetUserTotpEnabled) are performed as separate autocommits; wrap each pair in a
single DB transaction so both succeed or both roll back. Modify the TOTPVerified
branch to begin a transaction, call VerifyTotpProjection and SetUserTotpEnabled
inside that transaction and commit only if both succeed (rollback on error), and
do the same for the corresponding disable branch (the pair handling the opposite
projection and SetUserTotpEnabled(false)). Use the code paths/DB helpers already
available in this package (transaction begin/commit/rollback or the project's tx
helper) and keep the same logging on errors, but log and rollback the
transaction when either operation fails.

In `@internal/store/queries/totp.sql`:
- Around line 22-82: The statements must enforce projection_version monotonicity
to avoid stale replays: change UpsertTotpProjection's ON CONFLICT DO UPDATE to
only update when totp_projection.projection_version <
EXCLUDED.projection_version (add a WHERE totp_projection.projection_version <
EXCLUDED.projection_version clause), and modify the other exec queries
(VerifyTotpProjection, DeleteTotpProjection, MarkTotpBackupCodeUsed,
RegenerateTotpBackupCodes, SetUserTotpEnabled) to include a projection_version
comparison in their WHERE clauses (e.g. WHERE user_id = $1 AND
projection_version < $N) so you pass the event's projection_version as the
additional parameter and only apply the write when the stored projection_version
is older than the incoming one.

---

Nitpick comments:
In `@internal/projectors/totp_test.go`:
- Around line 29-175: Add a new test to exercise the ON CONFLICT "re-init" path:
after creating a user and running TestTotpListener_LifecycleEndToEnd’s
happy-path events (TOTPSetupInitiated -> TOTPVerified), append a second
TOTPSetupInitiated event with a different secret_encrypted and different
backup_codes_hash, then poll the totp projection (using pollForTotpRow) and
users projection (using pollForUserTotpEnabled) to assert that the totp row now
contains the new secret and new backup_codes_hash, that Verified and Enabled are
reset to false, and that BackupCodesUsed has been recreated with the new length
all set to false; add this as a separate test (e.g.,
TestTotpListener_ReinitOnConflict) or extend the lifecycle test to include this
second-setup scenario.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 44eab09d-4866-444c-b0dd-4f86a1e7abb0

📥 Commits

Reviewing files that changed from the base of the PR and between 201ae75 and bba83d0.

⛔ Files ignored due to path filters (1)
  • internal/store/generated/totp.sql.go is excluded by !**/generated/**
📒 Files selected for processing (6)
  • cmd/control/main.go
  • internal/projectors/totp.go
  • internal/projectors/totp_listener.go
  • internal/projectors/totp_test.go
  • internal/store/migrations/018_totp_projector_to_go.sql
  • internal/store/queries/totp.sql

Comment on lines +70 to +87
case "TOTPVerified":
if err := q.VerifyTotpProjection(ctx, db.VerifyTotpProjectionParams{
UserID: userID,
UpdatedAt: updatedAt,
ProjectionVersion: deref(e.SequenceNum),
}); err != nil {
logger.Warn("totp projector: failed to flip totp_projection to verified",
"event_id", e.ID, "user_id", userID, "error", err)
}
if err := q.SetUserTotpEnabled(ctx, db.SetUserTotpEnabledParams{
ID: userID,
TotpEnabled: true,
UpdatedAt: &updatedAt,
ProjectionVersion: deref(e.SequenceNum),
}); err != nil {
logger.Warn("totp projector: failed to flip users_projection.totp_enabled=TRUE",
"event_id", e.ID, "user_id", userID, "error", err)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Make the verify/disable dual writes atomic.

Both branches split the totp_projection change and the users_projection.totp_enabled flip into separate autocommit statements. If one succeeds and the other fails, the auth path can read the wrong TOTP state until a later event or rebuild repairs it. The old PL/pgSQL projector committed those effects together, so this port should preserve that by wrapping each pair in a transaction.

Suggested shape
- q := st.Queries()
+ q := st.Queries()

  switch e.EventType {
  case "TOTPVerified":
-     if err := q.VerifyTotpProjection(...); err != nil { ... }
-     if err := q.SetUserTotpEnabled(...); err != nil { ... }
+     if err := st.WithTx(ctx, func(q *generated.Queries) error {
+         if err := q.VerifyTotpProjection(ctx, ...); err != nil {
+             return err
+         }
+         return q.SetUserTotpEnabled(ctx, ...)
+     }); err != nil {
+         logger.Warn("totp projector: failed to apply TOTPVerified", ...)
+     }

  case "TOTPDisabled":
-     if err := q.DeleteTotpProjection(...); err != nil { ... }
-     if err := q.SetUserTotpEnabled(...); err != nil { ... }
+     if err := st.WithTx(ctx, func(q *generated.Queries) error {
+         if err := q.DeleteTotpProjection(ctx, ...); err != nil {
+             return err
+         }
+         return q.SetUserTotpEnabled(ctx, ...)
+     }); err != nil {
+         logger.Warn("totp projector: failed to apply TOTPDisabled", ...)
+     }

Also applies to: 89-102

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/projectors/totp_listener.go` around lines 70 - 87, The two related
writes (VerifyTotpProjection + SetUserTotpEnabled) are performed as separate
autocommits; wrap each pair in a single DB transaction so both succeed or both
roll back. Modify the TOTPVerified branch to begin a transaction, call
VerifyTotpProjection and SetUserTotpEnabled inside that transaction and commit
only if both succeed (rollback on error), and do the same for the corresponding
disable branch (the pair handling the opposite projection and
SetUserTotpEnabled(false)). Use the code paths/DB helpers already available in
this package (transaction begin/commit/rollback or the project's tx helper) and
keep the same logging on errors, but log and rollback the transaction when
either operation fails.

Comment on lines +22 to +82
-- name: UpsertTotpProjection :exec
INSERT INTO totp_projection (
user_id, secret_encrypted, verified, enabled,
backup_codes_hash, backup_codes_used,
created_at, updated_at, projection_version
) VALUES (
$1, $2, FALSE, FALSE,
$3,
array_fill(FALSE::boolean, ARRAY[array_length($3::text[], 1)]),
$4, $4, $5
)
ON CONFLICT (user_id) DO UPDATE SET
secret_encrypted = EXCLUDED.secret_encrypted,
verified = FALSE,
enabled = FALSE,
backup_codes_hash = EXCLUDED.backup_codes_hash,
backup_codes_used = EXCLUDED.backup_codes_used,
updated_at = EXCLUDED.updated_at,
projection_version = EXCLUDED.projection_version;

-- name: VerifyTotpProjection :exec
UPDATE totp_projection
SET verified = TRUE,
enabled = TRUE,
updated_at = $2,
projection_version = $3
WHERE user_id = $1;

-- name: DeleteTotpProjection :exec
DELETE FROM totp_projection WHERE user_id = $1;

-- TOTPBackupCodeUsed updates a 1-indexed slot in backup_codes_used.
-- The PL/pgSQL projector used `[(idx)::int + 1]` — we add the +1 in
-- Go before passing the parameter so the SQL stays clean.
--
-- name: MarkTotpBackupCodeUsed :exec
UPDATE totp_projection
SET backup_codes_used[$2::int] = TRUE,
updated_at = $3,
projection_version = $4
WHERE user_id = $1;

-- name: RegenerateTotpBackupCodes :exec
UPDATE totp_projection
SET backup_codes_hash = $2,
backup_codes_used = array_fill(FALSE::boolean, ARRAY[array_length($2::text[], 1)]),
updated_at = $3,
projection_version = $4
WHERE user_id = $1;

-- Cross-stream effect: TOTP events also flip
-- users_projection.totp_enabled. Was an inline UPDATE inside the
-- deleted PL/pgSQL projector. Sqlc'd here so the Go listener can
-- call it explicitly.
--
-- name: SetUserTotpEnabled :exec
UPDATE users_projection
SET totp_enabled = $2,
updated_at = $3,
projection_version = $4
WHERE id = $1;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Guard these writes with projection_version monotonicity.

These statements currently update/delete by user_id only, so a stale replay can roll the projection backwards. The clearest failure mode is an older TOTPDisabled deleting a row recreated by a newer TOTPSetupInitiated, but the same problem exists for the other branches too. The PR objective called out write-once/idempotent projector semantics, so these should be conditional on the stored projection_version being older than the event being applied.

Suggested shape
 -- name: UpsertTotpProjection :exec
 ...
 ON CONFLICT (user_id) DO UPDATE SET
     secret_encrypted = EXCLUDED.secret_encrypted,
     verified = FALSE,
     enabled = FALSE,
     backup_codes_hash = EXCLUDED.backup_codes_hash,
     backup_codes_used = EXCLUDED.backup_codes_used,
     updated_at = EXCLUDED.updated_at,
-    projection_version = EXCLUDED.projection_version;
+    projection_version = EXCLUDED.projection_version
+WHERE totp_projection.projection_version < EXCLUDED.projection_version;

 -- name: VerifyTotpProjection :exec
 UPDATE totp_projection
 SET verified = TRUE,
     enabled = TRUE,
     updated_at = $2,
     projection_version = $3
-WHERE user_id = $1;
+WHERE user_id = $1
+  AND projection_version < $3;

DeleteTotpProjection and SetUserTotpEnabled need the same treatment, which means passing the event sequence into the delete as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/store/queries/totp.sql` around lines 22 - 82, The statements must
enforce projection_version monotonicity to avoid stale replays: change
UpsertTotpProjection's ON CONFLICT DO UPDATE to only update when
totp_projection.projection_version < EXCLUDED.projection_version (add a WHERE
totp_projection.projection_version < EXCLUDED.projection_version clause), and
modify the other exec queries (VerifyTotpProjection, DeleteTotpProjection,
MarkTotpBackupCodeUsed, RegenerateTotpBackupCodes, SetUserTotpEnabled) to
include a projection_version comparison in their WHERE clauses (e.g. WHERE
user_id = $1 AND projection_version < $N) so you pass the event's
projection_version as the additional parameter and only apply the write when the
stored projection_version is older than the incoming one.

…ojection writes

Root cause: testutil.SetupPostgres did not register the Go-side
projector listeners that production boot wires in cmd/control/main.go.
Migration 018 turned project_totp_event into a no-op stub, so
without the Go listener firing in tests, no projector ran at all.
Tests that emitted a TOTP event and then read back from
totp_projection / users_projection saw stale state — manifesting as
TestVerifyTOTP_Success failing with 'TOTP not set up, call SetupTOTP
first', plus 3 sibling auth tests reading users_projection.totp_enabled.

Why this is the right fix vs the architectural alternatives we
considered (transactional projector dispatcher inside AppendEvent;
inline writes from each handler):

  - Store.fireListeners runs synchronously after the event commit
    and BEFORE AppendEvent returns. Once the listener is wired,
    handlers see the projection on the next line of code, same as
    they did under the deleted PL/pgSQL triggers.
  - The production behaviour was correct; only tests were broken.

internal/projectors/wire.go centralises the listener-registration
list so a new port (#98 through #106) only adds itself in one
place — testutil and production boot pick it up automatically.

Tests previously failing on PR #118 now pass:
  - TestVerifyTOTP_Success
  - TestVerifyTOTP_InvalidCode
  - TestLogin_TOTPRequired
  - TestListAuthMethods_WithEmailUserHasTOTP

Refs #97
@PaulDotterer PaulDotterer merged commit 6876e95 into main May 4, 2026
4 checks passed
@PaulDotterer PaulDotterer deleted the feat/97-totp-go-projector branch May 4, 2026 19:13
PaulDotterer added a commit that referenced this pull request Jun 14, 2026
Records the WS15 trust-model decisions (sdk #110 / agent #118): per-message
panic recovery + bounded fan-out, malformed-oneof nil-guards, inbound size
bound, terminal Cols/Rows bounds, fail-closed maintenance window, clock clamp,
crash marker, and the consolidated https gateway-URL guard. Implementation lives
in the sdk/agent repos; recorded here for trust-model continuity.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: port project_totp_event projector to Go event listener

1 participant