Skip to content

refactor: unify connection storage on agent_connections, drop chat_connections#506

Merged
buremba merged 1 commit into
mainfrom
refactor/unify-connection-storage
May 4, 2026
Merged

refactor: unify connection storage on agent_connections, drop chat_connections#506
buremba merged 1 commit into
mainfrom
refactor/unify-connection-storage

Conversation

@buremba
Copy link
Copy Markdown
Member

@buremba buremba commented May 3, 2026

Summary

Collapses two parallel connection-storage tables into one. ChatInstanceManager now reads/writes agent_connections directly via AgentConnectionStore instead of the separate chat_connections table + ChatConnectionStore shim. Secret fields stay behind secret:// ref indirection through SecretStoreRegistry so chat-platform tokens can be backed by Postgres / AWS Secrets Manager / Vault / k8s — same as every other secret category.

Commits

  • b1eef69d — unify connection storage
    • ChatInstanceManager switches from ChatConnectionStore to AgentConnectionStore.
    • Drops persistConnectionSnapshot dual-write from agent-routes.ts.
    • Migration drops chat_connections table.
    • listConnections() uses tryGetOrgId() so the manager can load connections at startup without org context.
    • Bundled small fixes: http-proxy early error/close handlers for blocked CONNECT sockets; embedded-deployment stops inheriting full gateway env into workers; core-services respects LOBU_PROVIDER_REGISTRY_PATH; agent_channel_bindings startup no longer deletes connections on secret resolution failure.
    • Initially inlined connection secrets as enc:v1: blobs — undone by the 41eb30a2 commit below.
  • 3dc03517 — delete dead ChatConnectionStore file (-128 lines).
  • 1f641b1a — fix stale persistConnectionSnapshot comment in test.
  • e1f55bf7 — Telegram: setWebhook failure no longer fatal at adapter startup; the adapter can still receive messages via an existing webhook configuration.
  • 41eb30a2 — restore secret-ref indirection for connection secrets
    • ChatInstanceManager.normalizeConfigForStorage moves plaintext secrets into the registry via secretStore.put("connections/<id>/<field>") and replaces values with secret://... refs before save. Idempotent — already-ref values pass through.
    • ChatInstanceManager.resolveConfigForRuntime materializes refs back to values inside startInstance before handing config to the Chat SDK adapter. Throws on unresolvable refs so a wiped secret surfaces as an errored connection rather than a half-baked boot.
    • removeConnection cascades deleteSecretsByPrefix("connections/<id>/") so torn-down connections don't leak secret rows. Add-connection rollback path also cleans up.
    • postgres-stores.ts saveConnection no longer encrypts — persists config JSON as-is. Read-side decryptLegacyEncryptedConfig keeps any pre-existing enc:v1: rows decryptable during transition; new writes never produce them.
    • Drive-by per-package typecheck fixes that pre-existed on this branch: getConnectionStore() added to CoreServices, stored.settings cast at storedToPlatform. Per-package bun run typecheck is now clean (the root tsconfig excludes owletto-backend, so PR-time checks weren't catching these).
    • Slack reload test now seeds a real PostgresSecretStore-backed ref so it actually exercises the new resolution path.

Net effect

~370 LOC net removed in the unification, restored ~165 LOC of ref-indirection (still net negative). One source of truth for connection rows. Connection secrets are now uniformly behind SecretStoreRegistry — same model as every other secret category, swappable backend.

Independent of #505 — file-overlap is core-services.ts only (one-line addition vs. #505's removals; clean rebase).

Test plan

  • Per-package bun run typecheck passes (was failing on this branch before — pre-existing issues fixed in 41eb30a2).
  • Root bun run typecheck passes.
  • bun test src/gateway/__tests__/ — 515 pass, 0 fail.
  • bun test src/lobu — 44 pass, 0 fail.
  • chat-instance-manager-slack reload test exercises real ref resolution (PostgresSecretStore + SecretStoreRegistry).
  • Verify chat_connections migration applies cleanly on existing deployments with chat-platform connections (decryptLegacyEncryptedConfig handles in-flight enc:v1: rows from anyone who built mid-PR).
  • Smoke-test Slack + Telegram + Discord connections after migration (create, list, restart, message round-trip).
  • Confirm secretsDeleted count > 0 in removeConnection log when the connection has tokens.
  • Confirm worker subprocesses no longer see gateway env vars they shouldn't.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@codecov-commenter
Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@buremba buremba enabled auto-merge (squash) May 4, 2026 01:06
buremba added a commit that referenced this pull request May 4, 2026
…efactor

Squash-rebase of PR #506 onto current main (post #499 package rename
owletto-backend → server). Combines the original 7-commit history into
one because the rename touched every file in the diff.

What changed:
- ChatInstanceManager reads/writes agent_connections directly via
  AgentConnectionStore. Drops the parallel chat_connections table +
  ChatConnectionStore shim. One source of truth.
- Connection secrets live as secret:// refs inside the row's config
  JSON, resolved at runtime through SecretStoreRegistry. Same model as
  every other secret category — pluggable to AWS Secrets Manager / Vault
  / k8s without per-platform code paths.
  - normalizeConfigForStorage moves plaintext secrets into the registry
    via secretStore.put('connections/<id>/<field>') before save.
    Idempotent (already-ref values pass through).
  - resolveConfigForRuntime materializes refs back to values inside
    startInstance, throws on unresolvable refs.
  - removeConnection cascades deleteSecretsByPrefix in safe order
    (history → secrets → row); addConnection rollback uses the same
    order.
  - updateConnection's needsRestart resolves previous refs before
    comparing against caller plaintext, so idempotent lobu-apply re-runs
    don't trip spurious restarts.
- Migration: copies any chat_connections rows into agent_connections
  (filtering NULL template_agent_id orphans) before DROP. Embedded
  PGlite path mirrors the same copy+drop wrapped in a pg_tables
  existence check so fresh installs skip cleanly.
- agent-routes.ts: no-manager fallback writes return 503 instead of
  persisting plaintext directly into agent_connections.config.
- postgres-stores.ts: stops encrypting writes; persists config JSON
  as-is. decryptLegacyEncryptedConfig fallback keeps any pre-existing
  enc:v1: rows decryptable for graceful transition.
- platform.ts: getConnectionStore() added to CoreServices interface.
- Bundled small fixes: Telegram non-fatal setWebhook, http-proxy early
  error handlers, embedded-deployment env hygiene, core-services
  LOBU_PROVIDER_REGISTRY_PATH respect, agent_channel_bindings startup
  no longer deletes connections on secret resolution failure.
- Drive-by typecheck fixes for pre-existing root-tsc errors in
  agent-worker that blocked the pre-commit hook (unrelated to this
  PR's scope, but required to land cleanly):
  - just-bash-bootstrap.ts: cast envRecord to NodeJS.ProcessEnv for
    execFile's env option (Vite ImportMetaEnv augments ProcessEnv with
    BASE_URL/MODE/DEV/PROD/SSR fields stripEnv's return type doesn't
    include).
  - openclaw/tools.ts: same cast on the spawnHook env return.

Verified:
- Per-package bun run typecheck exit 0.
- Root bunx tsc --noEmit exit 0.
- bun test src/gateway/__tests__/ src/lobu — 559 pass / 0 fail.
@buremba buremba force-pushed the refactor/unify-connection-storage branch from bc4f609 to f422d47 Compare May 4, 2026 01:17
@github-actions github-actions Bot added the triage:needs-human Triage agent escalated for human review label May 4, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

Triage decision: needs-human

Reasons:

  • PR size exceeds auto-merge threshold (1,105 lines; limit 300 lines)
  • Failed checks: pr-size, format-lint
  • Multiple CI checks still in progress (unit, integration, build-test, typecheck)

Next: Address failing checks, then merge manually after final review. Large PRs require human oversight regardless of bot approval status.

…efactor

Squash-rebase of PR #506 onto current main (post #499 package rename
owletto-backend → server). Combines the original 7-commit history into
one because the rename touched every file in the diff.

What changed:
- ChatInstanceManager reads/writes agent_connections directly via
  AgentConnectionStore. Drops the parallel chat_connections table +
  ChatConnectionStore shim. One source of truth.
- Connection secrets live as secret:// refs inside the row's config
  JSON, resolved at runtime through SecretStoreRegistry. Same model as
  every other secret category — pluggable to AWS Secrets Manager / Vault
  / k8s without per-platform code paths.
  - normalizeConfigForStorage moves plaintext secrets into the registry
    via secretStore.put('connections/<id>/<field>') before save.
    Idempotent (already-ref values pass through).
  - resolveConfigForRuntime materializes refs back to values inside
    startInstance, throws on unresolvable refs.
  - removeConnection cascades deleteSecretsByPrefix in safe order
    (history → secrets → row); addConnection rollback uses the same
    order.
  - updateConnection's needsRestart resolves previous refs before
    comparing against caller plaintext, so idempotent lobu-apply re-runs
    don't trip spurious restarts.
- Migration: copies any chat_connections rows into agent_connections
  (filtering NULL template_agent_id orphans) before DROP. Embedded
  PGlite path mirrors the same copy+drop wrapped in a pg_tables
  existence check so fresh installs skip cleanly.
- agent-routes.ts: no-manager fallback writes return 503 instead of
  persisting plaintext directly into agent_connections.config.
- postgres-stores.ts: stops encrypting writes; persists config JSON
  as-is. decryptLegacyEncryptedConfig fallback keeps any pre-existing
  enc:v1: rows decryptable for graceful transition.
- platform.ts: getConnectionStore() added to CoreServices interface.
- Bundled small fixes: Telegram non-fatal setWebhook, http-proxy early
  error handlers, embedded-deployment env hygiene, core-services
  LOBU_PROVIDER_REGISTRY_PATH respect, agent_channel_bindings startup
  no longer deletes connections on secret resolution failure.
- Drive-by typecheck fixes for pre-existing root-tsc errors in
  agent-worker that blocked the pre-commit hook (unrelated to this
  PR's scope, but required to land cleanly):
  - just-bash-bootstrap.ts: cast envRecord to NodeJS.ProcessEnv for
    execFile's env option (Vite ImportMetaEnv augments ProcessEnv with
    BASE_URL/MODE/DEV/PROD/SSR fields stripEnv's return type doesn't
    include).
  - openclaw/tools.ts: same cast on the spawnHook env return.

Verified:
- Per-package bun run typecheck exit 0.
- Root bunx tsc --noEmit exit 0.
- bun test src/gateway/__tests__/ src/lobu — 559 pass / 0 fail.
@buremba buremba force-pushed the refactor/unify-connection-storage branch from f422d47 to 44914b4 Compare May 4, 2026 01:32
@buremba buremba merged commit d6941ee into main May 4, 2026
13 of 15 checks passed
@buremba buremba deleted the refactor/unify-connection-storage branch May 4, 2026 01:33
buremba added a commit that referenced this pull request May 4, 2026
Biome wants the stripEnv call on a single line. Fixes the format-lint
failure on main introduced by #506.
buremba added a commit that referenced this pull request May 4, 2026
#506's chat-instance-manager.ts:initialize loads connections at gateway
boot and calls startInstance() → resolveConfigForRuntime() →
secretStore.get(). Since #516 made PostgresSecretStore resolve secrets
per-org via AsyncLocalStorage, boot-time reads have no org context, fall
back to the GLOBAL bucket, and find nothing — every connection written
under an org's context is then marked status='error' on first restart.

Resolve the agent's organization_id via getAgentOrganizationId() and
wrap the startInstance call in orgContext.run({organizationId}). Pre-org
rows (no template agent or no org) keep the GLOBAL bucket fallback.

Verified: bun test packages/server/src/gateway → 502 pass / 0 fail.
buremba added a commit that referenced this pull request May 4, 2026
… webhooks (#522)

* fix(chat-instance): wrap boot-time startInstance in orgContext

#506's chat-instance-manager.ts:initialize loads connections at gateway
boot and calls startInstance() → resolveConfigForRuntime() →
secretStore.get(). Since #516 made PostgresSecretStore resolve secrets
per-org via AsyncLocalStorage, boot-time reads have no org context, fall
back to the GLOBAL bucket, and find nothing — every connection written
under an org's context is then marked status='error' on first restart.

Resolve the agent's organization_id via getAgentOrganizationId() and
wrap the startInstance call in orgContext.run({organizationId}). Pre-org
rows (no template agent or no org) keep the GLOBAL bucket fallback.

Verified: bun test packages/server/src/gateway → 502 pass / 0 fail.

* chore: regen lockfile post-rebase onto #508
@buremba buremba restored the refactor/unify-connection-storage branch May 12, 2026 00:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage:needs-human Triage agent escalated for human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants