fix(webhooks): route webhook endpoints through tenant-aware engine methods#1388
Merged
nicoloboschi merged 1 commit intomainfrom May 4, 2026
Merged
fix(webhooks): route webhook endpoints through tenant-aware engine methods#1388nicoloboschi merged 1 commit intomainfrom
nicoloboschi merged 1 commit intomainfrom
Conversation
…thods
Webhook create/list/get/update/delete and list-deliveries endpoints in
the HTTP layer were calling pool.fetchrow/pool.fetch directly with
fq_table("webhooks"), bypassing the async-local schema context that
fq_table reads via get_current_schema(). Under deployments that set a
per-request target schema (multi-tenant routing), this caused webhooks
to be written to and read from the default schema while every other
operation on the same bank correctly resolved to the per-target
schema. Webhooks would land in the wrong schema; the fire path
(which uses the bank's resolved schema) would not see them and never
enqueued webhook_delivery operations -- silent failure, no errors.
Move the SQL into MemoryEngine methods that call _authenticate_tenant
first (matching the pattern used by retain/consolidate/mental-models),
so fq_table sees the same schema as the rest of the bank's data.
Add schema-isolation tests covering create/list/get/update/delete and
deliveries.
nicoloboschi
approved these changes
May 4, 2026
liling
pushed a commit
to liling/hindsight
that referenced
this pull request
May 5, 2026
…thods (vectorize-io#1388) Webhook create/list/get/update/delete and list-deliveries endpoints in the HTTP layer were calling pool.fetchrow/pool.fetch directly with fq_table("webhooks"), bypassing the async-local schema context that fq_table reads via get_current_schema(). Under deployments that set a per-request target schema (multi-tenant routing), this caused webhooks to be written to and read from the default schema while every other operation on the same bank correctly resolved to the per-target schema. Webhooks would land in the wrong schema; the fire path (which uses the bank's resolved schema) would not see them and never enqueued webhook_delivery operations -- silent failure, no errors. Move the SQL into MemoryEngine methods that call _authenticate_tenant first (matching the pattern used by retain/consolidate/mental-models), so fq_table sees the same schema as the rest of the bank's data. Add schema-isolation tests covering create/list/get/update/delete and deliveries.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The HTTP webhook endpoints (
create_webhook,list_webhooks,update_webhook,delete_webhook,list_webhook_deliveries) inhindsight-api-slim/hindsight_api/api/http.pyresolvedfq_table("webhooks")before any tenant authentication, doing rawpool.fetchrow/pool.fetchcalls directly.fq_tablereads the target schema from async-local context (get_current_schema()), which is set per-request by_authenticate_tenant. Skipping that step caused these endpoints to fall through to the default schema regardless of any per-request schema resolution.Under any deployment that uses async-local context to set a per-request target schema (multi-tenant routing), this meant webhook rows were silently written to and read from the default schema while every other operation on the same bank —
retain,consolidate,mental-models, etc. — correctly resolved to the per-target schema. The fire path then never matched the webhooks (it looks them up in the bank's resolved schema) and nowebhook_deliveryasync operations were ever enqueued. The failure was completely silent: no errors, the webhook lists/creates/updates fine via the API, but no events ever fire.Fix
Move the SQL into
MemoryEnginemethods (create_webhook,list_webhooks,update_webhook,delete_webhook,list_webhook_deliveries) that call_authenticate_tenant(request_context)first — matching the pattern already used by retain, consolidate, mental-models, etc. The HTTP handlers now delegate to those engine methods.fq_table("webhooks")resolves through the same schema contextvar as the rest of the bank's data.The fire path itself is unchanged —
_webhook_manager.fire_event_with_connalready takes its ownschema=parameter.Test plan
tests/test_webhooks.pycovering create / list / update / delete / list-deliveries — each provisions a non-default schema viarun_migrationsand a swapped_tenant_extension, then asserts the relevant SQL targets the resolved schema and not the default.tests/test_webhooks.pycases still pass (37/37 total).tests/test_audit_log.py(17/17) andtests/test_schema_isolation.py(4/4) clean.test_extensions.py::test_reflect_validationreproduces identically onmain— unrelated to this change.Notes
http.pythat follow a similar shape (_get_backend()thenfq_table(...)without an engine method indirection) were spot-checked but not exhaustively audited — worth a follow-up grep job forfrom hindsight_api.engine.memory_engine import fq_tableinsidehttp.pyto confirm webhooks were the only case where the schema context was missed before SQL was emitted.