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
11 changes: 11 additions & 0 deletions docs/production-runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ Because the current image entrypoint always runs `prisma migrate deploy`, every

For multi-replica production deployments, avoid migration races by ensuring only one instance starts with the migration-capable entrypoint at a time, or by using platform controls/entrypoint overrides to run a dedicated migration job before scaling the web app. If your platform cannot separate migrations from app startup, document that limitation in the release notes and roll out one replica at a time.

### Legacy pre-token database upgrades

The migration chain has a compatibility path for older non-empty AgentBridge databases that existed before company-scoped bearer tokens and stable `AgentId` values were introduced. Historical token migrations backfill deterministic placeholder values so `prisma migrate deploy` can complete instead of failing on required non-null columns.

After upgrading a legacy pre-token database:

1. Sign in to the dashboard.
2. Review agents and rename any generated `legacy-<uuid>` AgentIds to the intended stable API identifiers before configuring external agents.
3. Rotate the company bearer token in Settings and update external agent configs with the newly shown token. The migration placeholders are not usable plaintext bearer tokens.
4. Run the Agent API smoke check with the intended `AgentId` and new company token.

## App start

For Docker Compose with an external database:
Expand Down
20 changes: 15 additions & 5 deletions prisma/migrations/20260508210351_add_bearer_token/migration.sql
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
/*
Warnings:

- A unique constraint covering the columns `[bearerTokenHash]` on the table `Agent` will be added. If there are existing duplicate values, this will fail.
- Added the required column `bearerTokenHash` to the `Agent` table without a default value. This is not possible if the table is not empty.
Historical compatibility note:

This migration originally added Agent.bearerTokenHash as NOT NULL without a
default, which fails when replayed against pre-token AgentBridge databases
that already contain Agent rows. The column is removed by
20260510055256_refactor_token, so the placeholder below only makes legacy
migration replay possible and is not used by the current application model.
*/
-- AlterTable
ALTER TABLE "Agent" ADD COLUMN "bearerTokenHash" TEXT NOT NULL;
ALTER TABLE "Agent" ADD COLUMN "bearerTokenHash" TEXT;

-- Backfill deterministic unique placeholders for legacy non-empty databases.
UPDATE "Agent"
SET "bearerTokenHash" = 'legacy-agent-token-' || "id"::text
WHERE "bearerTokenHash" IS NULL;

-- Enforce the intended required constraint after backfill.
ALTER TABLE "Agent" ALTER COLUMN "bearerTokenHash" SET NOT NULL;

-- CreateIndex
CREATE UNIQUE INDEX "Agent_bearerTokenHash_key" ON "Agent"("bearerTokenHash");
32 changes: 23 additions & 9 deletions prisma/migrations/20260510055256_refactor_token/migration.sql
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
/*
Warnings:

- You are about to drop the column `bearerTokenHash` on the `Agent` table. All the data in the column will be lost.
- A unique constraint covering the columns `[AgentId]` on the table `Agent` will be added. If there are existing duplicate values, this will fail.
- A unique constraint covering the columns `[bearerTokenHash]` on the table `Company` will be added. If there are existing duplicate values, this will fail.
- Added the required column `AgentId` to the `Agent` table without a default value. This is not possible if the table is not empty.
- Added the required column `bearerTokenHash` to the `Company` table without a default value. This is not possible if the table is not empty.
Historical compatibility note:

This migration originally added required Agent.AgentId and
Company.bearerTokenHash columns without defaults, which fails when replayed
against non-empty pre-refactor databases. The deterministic backfill below
preserves migration replay safety without exposing real bearer tokens.
Operators should rotate the generated company token after upgrading any
legacy database that crossed this migration boundary.
*/
-- DropIndex
DROP INDEX "Agent_bearerTokenHash_key";

-- AlterTable
ALTER TABLE "Agent" DROP COLUMN "bearerTokenHash",
ADD COLUMN "AgentId" TEXT NOT NULL;
ADD COLUMN "AgentId" TEXT;

-- Backfill stable AgentId values for legacy rows before enforcing NOT NULL.
UPDATE "Agent"
SET "AgentId" = 'legacy-' || "id"::text
WHERE "AgentId" IS NULL;

ALTER TABLE "Agent" ALTER COLUMN "AgentId" SET NOT NULL;

-- AlterTable
ALTER TABLE "Company" ADD COLUMN "bearerTokenHash" TEXT NOT NULL;
ALTER TABLE "Company" ADD COLUMN "bearerTokenHash" TEXT;

-- Backfill deterministic unique placeholders for legacy company rows.
UPDATE "Company"
SET "bearerTokenHash" = 'legacy-company-token-' || "id"::text
WHERE "bearerTokenHash" IS NULL;

ALTER TABLE "Company" ALTER COLUMN "bearerTokenHash" SET NOT NULL;

-- CreateIndex
CREATE UNIQUE INDEX "Agent_AgentId_key" ON "Agent"("AgentId");
Expand Down
Loading