Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ handful of domain packages built on top.
`@intx/hub-api` routes (auth, tenants, principals, grants, workflow
deploy/run) plus one composition block that adds Corbits libraries —
`@corbits/mailbox` (chat), `@corbits/memory` (recall), `@corbits/artifacts`
(files), `@corbits/cron` (scheduling) — and the remaining in-repo
packages: `webhook-triggers` (inbound webhook ingress), `workflows`
(author routes), and `agent-directory`. Credentials are stored through
(files), `@corbits/cron` (scheduling), `@corbits/webhooks` (inbound
webhook ingress) — and the remaining in-repo packages: `workflows`
(author routes) and `agent-directory`. Credentials are stored through
Interchange's own stock credential routes; nothing in this repo wraps
or replaces them.
- **`apps/sidecar`** — a byte-for-byte copy of Interchange's own sidecar.
Expand Down Expand Up @@ -48,7 +48,7 @@ stock workflow-deploy route. `tools/*` and `skills/*` are ordinary

## Data

Custom tables — `webhook-triggers`, `agent-directory`, and so on — live
Custom tables — `agent-directory` and so on — live
on their own Postgres schema, each with foreign keys back to
Interchange's `tenant`/`principal` tables in `public`. Each package ships
one idempotent migration, which the hub applies itself at boot. Anything
Expand Down
2 changes: 1 addition & 1 deletion apps/hub/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"@corbits/mailbox": "github:corbitsdev/corbits-mailbox#e85b81e4a2e0cc318dd33a4f0f92337b6fac7f38",
"@corbits/memory": "github:corbitsdev/corbits-memory#e74da20f148a302dff5400915fe504ee2395e913",
"@corbits/url-path": "workspace:*",
"@corbits/webhook-triggers": "workspace:*",
"@corbits/webhooks": "github:corbitsdev/webhooks#570bd52688920c0ce018a8fbcd046199fa4125dd",
"@corbits/workflows": "workspace:*",
"@corbits/xai-provider": "github:corbitsdev/corbits-xai-provider#9f2d4bac40ea075df092fef807404a13c638cf14",
"@intx/authz": "0.3.0",
Expand Down
7 changes: 3 additions & 4 deletions apps/hub/src/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
// service, no ledger table, just the hub doing the work itself.
import { runMigrations } from "@intx/db";
import { applyCronMigrations } from "@corbits/cron";
import { applyWebhookTriggersMigrations } from "@corbits/webhook-triggers";
import { createMailboxDb, runMailboxMigrations } from "@corbits/mailbox";
import { runArtifactMigrations } from "@corbits/artifacts";
import { runMemoryMigrations } from "@corbits/memory";
Expand All @@ -30,8 +29,9 @@ function databaseUrlFrom(config: HubMigrateConfig): string {

/**
* Apply the platform schema plus every mounted Corbits package's own
* migration, in the order the hub mounts them: cron, webhook-triggers,
* mailbox, artifacts, memory. `db` is the same drizzle handle the rest
* migration, in the order the hub mounts them: cron, mailbox, artifacts,
* memory. `@corbits/webhooks` owns no table of its own, so it has no
* migration step. `db` is the same drizzle handle the rest
* of the hub uses, so `runArtifactMigrations` (which takes a drizzle
* db, not a URL) shares the one connection pool rather than opening
* its own.
Expand All @@ -45,7 +45,6 @@ export async function migrateHub(

await runMigrations(config, { schema });
await applyCronMigrations(databaseUrl, { tenantSchema: schema });
await applyWebhookTriggersMigrations(databaseUrl);

const mailboxDb = createMailboxDb(databaseUrl);
try {
Expand Down
76 changes: 25 additions & 51 deletions apps/hub/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {
resolveFrameSenderKey,
resolveSenderKey,
} from "@intx/db";
import { tenant as tenantTable, workflowDefinition } from "@intx/db/schema";
import { and, eq } from "drizzle-orm";
import { tenant as tenantTable } from "@intx/db/schema";
import { eq } from "drizzle-orm";
import { createEnvKeyCredentialCipher, sha256 } from "@intx/crypto";
import { hexDecode, hexEncode, type SidecarCapabilityRule } from "@intx/types";
import {
Expand Down Expand Up @@ -69,13 +69,7 @@ import {
createHubMailboxAuthorizeSender,
createHubPersistMailWithSessionEnsure,
} from "./mailbox-persist";
import {
createDrizzleWebhookTriggerStore,
createWebhookIngressRoutes,
createWebhookTriggerRoutes,
launchWebhookTrigger,
createCryptoProviderCache,
} from "@corbits/webhook-triggers";
import { installWebhooks, type HookMailRouter } from "@corbits/webhooks";
import { createWorkflowAuthorRegistry, createWorkflowAuthorRoutes } from "@corbits/workflows";
import {
createProcessSidecarProvisioner,
Expand Down Expand Up @@ -622,48 +616,28 @@ export async function createHubServer({
}),
);

const webhookTriggerStore = createDrizzleWebhookTriggerStore(db, credentialCipher);
const cryptoProviders = createCryptoProviderCache();
app.route(
`${TENANT_PREFIX}/webhook-triggers`,
createWebhookTriggerRoutes({
store: webhookTriggerStore,
requireGrant: createRequireGrant({ grantStore, conditionRegistry: grantConditionRegistry }),
workflowDefinitionInTenant: async (tenantId, definitionId) => {
const row = await db.query.workflowDefinition.findFirst({
where: and(
eq(workflowDefinition.id, definitionId),
eq(workflowDefinition.tenantId, tenantId),
),
columns: { id: true },
});
return row !== undefined;
},
}),
);
const isSidecarRoutable = (address: string) =>
sidecarRouter.getRoutableAddresses().includes(address);
app.route(
"/api/webhooks",
createWebhookIngressRoutes({
store: webhookTriggerStore,
launch: (trigger, payload) =>
launchWebhookTrigger(
{
db,
sidecarRouter,
repoStore: agentRepoStore.repoStore,
workflowAllocationService,
credentialCipher,
eventCollectors,
isRoutable: isSidecarRoutable,
cryptoProviderCache: cryptoProviders,
},
trigger,
payload,
),
}),
);
// `HookMailRouter` types its payloads as `unknown` at the package
// boundary; this just narrows them back to `sidecarRouter`'s own types
// on the way through, with no behavior change.
const webhookMailRouter: HookMailRouter = {
routeMail: (address, rawMessage, authenticatedSender, messageId) =>
sidecarRouter.routeMail(address, rawMessage, authenticatedSender, messageId),
sendRunGrants: (address, runId, stepGrants, senderIdentities) =>
sidecarRouter.sendRunGrants(
address,
runId,
stepGrants as Parameters<typeof sidecarRouter.sendRunGrants>[2],
senderIdentities as Parameters<typeof sidecarRouter.sendRunGrants>[3],
),
};

await installWebhooks({
app,
db,
credentialCipher,
principalKeyStore,
router: webhookMailRouter,
});

// ---------------------------------------------------------------------
// End of Corbits mount block.
Expand Down
2 changes: 0 additions & 2 deletions apps/web/src/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ export { RolesSection, RolesTable, RoleAssignments, CreateRoleDialog } from "./r
export { GrantsSection, GrantsTable, CreateGrantDialog } from "./grants-section";
export { CredentialsSection } from "./credentials-section";

export { CopyButton, CopyableCodeRow, WebhookSecretPanel } from "./webhook-secret-panel";

export { grantPreviewSentence, expiryIsoFromPreset, expiryLabelFromPreset } from "./grant-preview";
export type { GrantPreviewInput } from "./grant-preview";
export { KindCards } from "./kind-cards";
Expand Down
50 changes: 2 additions & 48 deletions apps/web/src/settings/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -592,52 +592,6 @@
opacity: 0.75;
}

.settings-webhook-field {
display: flex;
flex-direction: column;
gap: 0.25rem;
}

.settings-webhook-field-label {
font-size: 0.75rem;
font-weight: 500;
}

.settings-webhook-field-box {
display: flex;
align-items: center;
gap: 0.375rem;
border: 1px solid var(--border);
border-radius: 0;
background: color-mix(in srgb, var(--foreground) 4%, transparent);
padding: 0.375rem 0.625rem;
}

.settings-webhook-field-code {
min-width: 0;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: var(--font-mono, ui-monospace, monospace);
font-size: 0.75rem;
}

.settings-webhook-routine-row {
display: flex;
flex-direction: column;
gap: 0.5rem;
border: 1px solid var(--border);
border-radius: var(--radius, 0);
padding: 0.75rem;
}

.settings-webhook-routine-row-header {
display: flex;
flex-direction: column;
gap: 0.25rem;
}

.settings-advanced-disclosure {
border-top: 1px solid var(--border);
padding-top: 0.75rem;
Expand Down Expand Up @@ -673,8 +627,8 @@
}

/* Square, not pill-shaped — consistent with this app's other react-ui
primitive overrides (e.g. .settings-webhook-field-box above), applied
here so every Switch in settings-ui gets it, not just this one. */
primitive overrides, applied here so every Switch in settings-ui gets
it, not just this one. */
[role="switch"],
[role="switch"] > span {
border-radius: 0;
Expand Down
111 changes: 0 additions & 111 deletions apps/web/src/settings/webhook-secret-panel.tsx

This file was deleted.

Loading
Loading