Problem and intended outcome
Octostaff, particularly its Bubble server, is intended to run as a Huabu plugin. We want one application identity and a shared storage foundation, preferably served by one HTTP/WebSocket server. Users should not need separate Huabu and Bubble accounts.
This issue records the architectural direction discussed and the remaining design work. It is based on source inspection, not a tested integration or a finalized implementation specification.
Proposed ownership
Auth0 or Microsoft Entra ID should provide the external authentication foundation. The application still needs a durable principal directory for ownership, permissions, agent identities, and historical authorship. Reusing Bubble's directory is a simpler initial design than introducing a separate Huabu user system or immediately extracting another identity package.
| Responsibility |
Proposed owner |
| External login, passwords, MFA |
Auth0 / Entra ID |
| Credential validation and external identity-to-principal mapping |
Bubble |
| Shared application principal IDs, profiles, account status, bot credentials |
Bubble |
| Thread grants and participation |
Bubble |
| Huabu-specific preferences and records |
Huabu, referencing Bubble principal IDs |
| Definition and enforcement of Huabu feature permissions |
Huabu, using Bubble's policy where supported |
| Database/blob provisioning, configuration, connection lifetime |
Huabu |
| Domain tables, queries, migrations, retention |
The module owning the data |
| Server listener and overall startup/shutdown |
Huabu |
Huabu can have principal-related tables without maintaining another account system. For example, Huabu preferences reference the shared principalId.
This makes Bubble's identity services a required backend dependency, even if the chat UI is optional. If Bubble must be fully removable while Huabu identity continues working, revisit extracting a reusable identity module. That extraction is not necessary for the initial integration if the dependency is intentional.
The composition order is storage → Bubble services → Huabu routes → listening server. Huabu supplying storage while consuming Bubble services is ordinary dependency injection; Bubble does not need to import Huabu.
Authentication and application user data
- Use
(issuer, subject) → principalId for external identity mapping. Bubble currently stores a unique raw subject; issuer qualification is needed for a shared multi-provider directory. Do not use email as the identity key or automatically merge accounts by email.
- Preserve existing principal IDs during migration so grants and historical event authorship remain valid.
- Decide whether name/avatar/email follow the identity provider or allow application overrides. Bubble currently prefers existing stored profile fields on subsequent authentication; automatic profile synchronization is not established.
- Define human account suspension, logout, token/session revocation, and their effects on existing WebSocket connections. Existing bot/token lifecycle behavior is not a complete human-account lifecycle.
- Explicitly provision administrators and disable first-user system-owner auto-grant in the embedded deployment.
- Keep local desktop access as an explicit identity mode. Do not translate every loopback request into an unrestricted human owner in a hosted deployment.
- Host one browser login/session flow. Bubble validates credentials; Starfish currently supplies its own Next.js Auth0 login integration, which cannot simply be assumed to exist inside Huabu.
Authorization: what can be reused
Bubble already supports additional capability names on its existing roles through RoleCapabilityContribution. Huabu could reuse this for instance-level permissions such as huabu.settings.manage, explicitly checked against the system target.
Full Huabu authorization needs more work: Bubble's targets are currently system, thread, and principal. Workspace and Space targets are absent. Centralizing all authorization in Bubble therefore requires extending the target model, persistence, and inheritance rules. Do not represent Spaces as artificial threads just to fit the existing model.
Authentication alone must not grant Huabu owner access. Replace current owner shortcuts with explicit checks at Huabu service boundaries, including in-process calls. Define how Space permissions relate to attached Bubble threads; the proposed default is that both containing-resource access and the relevant Bubble capability are required. Any broader sharing should be intentional.
Shared storage
Huabu should provision database resources and supply them to Bubble. Bubble should retain its domain repositories, migrations, transactions, event ordering, leases, and subscription semantics.
Concrete gaps:
- Huabu uses
pg.Pool; Bubble uses the postgres tagged-SQL driver and a dedicated listening connection. Huabu's existing pool cannot be passed directly to Bubble. Initially, Huabu can provision both drivers against one database with separate schemas and a coordinated connection budget. Sharing a single pool requires standardizing the driver.
- Bubble currently closes its underlying connections when its store closes. Add explicit owned/borrowed resource semantics so a plugin cannot close host-owned shared resources.
- Huabu's existing
Space.extension() substrate is tied to Space existence and deletion. Shared principals and longer-lived plugin state need installation-level storage, with Workspace scope where appropriate.
- Sharing a database does not establish a cross-domain transaction. Operations spanning Huabu and Bubble need explicit atomicity or an idempotent recovery workflow.
- Bubble currently has memory and PostgreSQL structured stores, but no SQLite adapter. Durable local desktop support requires a SQLite Bubble adapter, including enabled extensions, or an explicit remote-service deployment.
For blobs, share disk/Azure infrastructure beneath domain adapters. Huabu's blob contract uses flat names in Space areas and whole-area deletion; Bubble artifacts support per-object deletion and optional direct transfers. Preserve separate namespaces, access checks, and retention ownership. Bubble's core thread enclosures currently store bytes in PostgreSQL; moving those is a separate decision because it changes transactional attachment behavior.
One server is feasible
Both applications use Fastify 5 with overlapping dependency ranges. There is no fundamental framework mismatch, but the current entrypoints are not directly composable: Bubble's buildBubbleServer() creates its own instance, calls ready(), and starts background workers.
Refactor Bubble into:
- A supported runtime factory exposing application services, authentication, policy, and lifecycle.
- A Fastify plugin registering HTTP/WebSocket routes on a supplied instance.
- A standalone builder/launcher composing the same pieces.
Huabu can then construct one Bubble runtime, call its services in-process, and mount its routes alongside Huabu routes on one listener. No internal HTTP hop is needed.
Integration must reconcile Huabu's global Basic Auth gate, restricted agent credentials, CORS/origin protection, WebSocket registration, route prefixes, scoped error handling, and SPA fallback. Workers should start after registration and stop before storage closes. Align framework/plugin dependencies and verify existing SDK route compatibility.
Workspace boundary
Huabu currently has one process-wide active Workspace. Separate logins do not create independent Workspace contexts. A fixed Workspace per hosted deployment is a possible initial boundary; simultaneous independent Workspace access requires explicitly scoped handles in requests and background work.
Define how Bubble's instance-wide system grants and principal discovery interact with that boundary. Database namespaces alone do not enforce tenant isolation.
Decisions to settle
- Is Bubble's identity runtime a mandatory Huabu dependency, even when chat features are disabled?
- Should the first integration use Bubble only for identity/system permissions, or also extend it to own Workspace/Space grants?
- Is the initial target a fixed-Workspace PostgreSQL deployment, desktop-local persistence, or both?
- Is one database with separately managed driver pools sufficient initially, or is one shared driver/pool required?
- What are the profile synchronization, account linking, administrator bootstrap, revocation, and thread-sharing rules?
Suggested delivery and verification
First establish shared principal resolution and the mountable Bubble runtime. Wire host-provided PostgreSQL resources and explicit Huabu permission checks. Address Workspace-scoped authorization and desktop SQLite according to the decisions above.
Integration proof should cover the same principal across both APIs; denied unauthorized Huabu and Bubble operations; HTTP/WebSocket revocation; persistence across restart; safe plugin shutdown; object/Space deletion ownership; and route/SDK behavior on the combined server.
Source inspection anchors
Inspected Huabu at 75979b9e74d4453b3b7413fc42476ed2a0c4c2bd and Octostaff umbrella at 2eb3a80c6a57489907642d36fc46b5d497311765.
- Huabu:
apps/server/src/app.ts, modules/security/owner.ts, modules/storage/ports/structured.ts, modules/storage/ports/blob.ts, modules/storage/storage.ts, and modules/storage/backends/postgres/database.ts (module paths relative to apps/server/src/).
- Octostaff:
packages/bubble/src/server/index.ts, application/identity.ts, store/postgres/{index,connection,schema}.ts (latter paths relative to packages/bubble/src/); packages/sdk-typescript/src/types/authz.ts; packages/sdk-typescript/src/authz/roles.ts; packages/starfish/src/lib/auth/auth0.ts.
No implementation changes or combined-server tests have been performed for this investigation.
— posted by Codex (via the issue-tracker skill)
Problem and intended outcome
Octostaff, particularly its Bubble server, is intended to run as a Huabu plugin. We want one application identity and a shared storage foundation, preferably served by one HTTP/WebSocket server. Users should not need separate Huabu and Bubble accounts.
This issue records the architectural direction discussed and the remaining design work. It is based on source inspection, not a tested integration or a finalized implementation specification.
Proposed ownership
Auth0 or Microsoft Entra ID should provide the external authentication foundation. The application still needs a durable principal directory for ownership, permissions, agent identities, and historical authorship. Reusing Bubble's directory is a simpler initial design than introducing a separate Huabu user system or immediately extracting another identity package.
Huabu can have principal-related tables without maintaining another account system. For example, Huabu preferences reference the shared
principalId.This makes Bubble's identity services a required backend dependency, even if the chat UI is optional. If Bubble must be fully removable while Huabu identity continues working, revisit extracting a reusable identity module. That extraction is not necessary for the initial integration if the dependency is intentional.
The composition order is storage → Bubble services → Huabu routes → listening server. Huabu supplying storage while consuming Bubble services is ordinary dependency injection; Bubble does not need to import Huabu.
Authentication and application user data
(issuer, subject) → principalIdfor external identity mapping. Bubble currently stores a unique raw subject; issuer qualification is needed for a shared multi-provider directory. Do not use email as the identity key or automatically merge accounts by email.Authorization: what can be reused
Bubble already supports additional capability names on its existing roles through
RoleCapabilityContribution. Huabu could reuse this for instance-level permissions such ashuabu.settings.manage, explicitly checked against the system target.Full Huabu authorization needs more work: Bubble's targets are currently
system,thread, andprincipal. Workspace and Space targets are absent. Centralizing all authorization in Bubble therefore requires extending the target model, persistence, and inheritance rules. Do not represent Spaces as artificial threads just to fit the existing model.Authentication alone must not grant Huabu owner access. Replace current owner shortcuts with explicit checks at Huabu service boundaries, including in-process calls. Define how Space permissions relate to attached Bubble threads; the proposed default is that both containing-resource access and the relevant Bubble capability are required. Any broader sharing should be intentional.
Shared storage
Huabu should provision database resources and supply them to Bubble. Bubble should retain its domain repositories, migrations, transactions, event ordering, leases, and subscription semantics.
Concrete gaps:
pg.Pool; Bubble uses thepostgrestagged-SQL driver and a dedicated listening connection. Huabu's existing pool cannot be passed directly to Bubble. Initially, Huabu can provision both drivers against one database with separate schemas and a coordinated connection budget. Sharing a single pool requires standardizing the driver.Space.extension()substrate is tied to Space existence and deletion. Shared principals and longer-lived plugin state need installation-level storage, with Workspace scope where appropriate.For blobs, share disk/Azure infrastructure beneath domain adapters. Huabu's blob contract uses flat names in Space areas and whole-area deletion; Bubble artifacts support per-object deletion and optional direct transfers. Preserve separate namespaces, access checks, and retention ownership. Bubble's core thread enclosures currently store bytes in PostgreSQL; moving those is a separate decision because it changes transactional attachment behavior.
One server is feasible
Both applications use Fastify 5 with overlapping dependency ranges. There is no fundamental framework mismatch, but the current entrypoints are not directly composable: Bubble's
buildBubbleServer()creates its own instance, callsready(), and starts background workers.Refactor Bubble into:
Huabu can then construct one Bubble runtime, call its services in-process, and mount its routes alongside Huabu routes on one listener. No internal HTTP hop is needed.
Integration must reconcile Huabu's global Basic Auth gate, restricted agent credentials, CORS/origin protection, WebSocket registration, route prefixes, scoped error handling, and SPA fallback. Workers should start after registration and stop before storage closes. Align framework/plugin dependencies and verify existing SDK route compatibility.
Workspace boundary
Huabu currently has one process-wide active Workspace. Separate logins do not create independent Workspace contexts. A fixed Workspace per hosted deployment is a possible initial boundary; simultaneous independent Workspace access requires explicitly scoped handles in requests and background work.
Define how Bubble's instance-wide system grants and principal discovery interact with that boundary. Database namespaces alone do not enforce tenant isolation.
Decisions to settle
Suggested delivery and verification
First establish shared principal resolution and the mountable Bubble runtime. Wire host-provided PostgreSQL resources and explicit Huabu permission checks. Address Workspace-scoped authorization and desktop SQLite according to the decisions above.
Integration proof should cover the same principal across both APIs; denied unauthorized Huabu and Bubble operations; HTTP/WebSocket revocation; persistence across restart; safe plugin shutdown; object/Space deletion ownership; and route/SDK behavior on the combined server.
Source inspection anchors
Inspected Huabu at
75979b9e74d4453b3b7413fc42476ed2a0c4c2bdand Octostaff umbrella at2eb3a80c6a57489907642d36fc46b5d497311765.apps/server/src/app.ts,modules/security/owner.ts,modules/storage/ports/structured.ts,modules/storage/ports/blob.ts,modules/storage/storage.ts, andmodules/storage/backends/postgres/database.ts(module paths relative toapps/server/src/).packages/bubble/src/server/index.ts,application/identity.ts,store/postgres/{index,connection,schema}.ts(latter paths relative topackages/bubble/src/);packages/sdk-typescript/src/types/authz.ts;packages/sdk-typescript/src/authz/roles.ts;packages/starfish/src/lib/auth/auth0.ts.No implementation changes or combined-server tests have been performed for this investigation.
— posted by Codex (via the issue-tracker skill)