From e77f6b460bcf55c296f618ba1368f9e837a3a8a7 Mon Sep 17 00:00:00 2001 From: Sean Matthews Date: Thu, 1 Oct 2026 16:07:14 -0400 Subject: [PATCH 1/8] docs: add What's New in Frigg 2.0 Rewrite the July draft against the code on next. Fix the database selection (DB_TYPE or Definition.database, DATABASE_URL is only the connection string), describe reports as admin operations, and drop the features that have not shipped. Add the shipped work the draft missed: admin scripts, telemetry, structured logging, SSM offload, token refresh reliability, atomic config and process updates, queryMappings, and the final SQS attempt signal. Co-Authored-By: Claude Opus 5.5 --- docs/getting-started/whats-new-in-2.0.md | 222 +++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 docs/getting-started/whats-new-in-2.0.md diff --git a/docs/getting-started/whats-new-in-2.0.md b/docs/getting-started/whats-new-in-2.0.md new file mode 100644 index 000000000..a42a81a0e --- /dev/null +++ b/docs/getting-started/whats-new-in-2.0.md @@ -0,0 +1,222 @@ +--- +description: >- + Frigg 2.0 is the production foundation: your choice of database, real + operations tooling, admin work inside your VPC, and integrations that stay + connected. +--- + +# What's New in Frigg 2.0 + +Frigg 2.0 is the production foundation for native integrations. You run it on +PostgreSQL, MongoDB, or DocumentDB. You deploy, check, and repair it with one +CLI. You run admin scripts and reports inside your own VPC. Your integrations +refresh tokens, retry, and record their state without losing writes. + +{% hint style="warning" %} +2.0 has breaking changes. If you run a 1.x app, read +[Migrating from 1.x to 2.0](../guides/migrating-to-2.0.md) before you upgrade. +{% endhint %} + +## Breaking changes at a glance + +* **Mongoose is gone.** Data access runs on Prisma. Core no longer exports + `mongoose`, the Mongoose models, `connectToDatabase`, `createFriggBackend`, + `ModuleManager`, `Auther`, or `IntegrationFactory`. +* **You pick the database explicitly.** Set `database.postgres.enable`, + `database.mongoDB.enable`, or `database.documentDB.enable` in your app + definition, or set `DB_TYPE`. `DATABASE_URL` is the connection string only. + Frigg throws at startup if neither tells it the engine. +* **Integration classes use `static Definition`.** The 1.x `static Config`, + `static Options`, and `static modules` are replaced by one `Definition` with + `name`, `version`, `display`, `modules`, and `routes`. Events go in + `this.events`. +* **One `User` model.** The `IndividualUser` and `OrganizationUser` + discriminators are replaced by a `User` model with a `type` field. +* **New integration statuses.** Integrations start in `IN_CREATION` and pass + through `IN_DELETION` on teardown. Code that filters on status must handle + both. +* **Node.js 22, AWS SDK v3, and osls.** Lambda functions run on `nodejs22.x`. + Deploys use oss-serverless (`osls`) instead of the Serverless Framework. + +## Requirements + +| Requirement | Frigg 2.0 | +| --- | --- | +| Node.js | 22 or later | +| npm | 10 or later | +| Database | PostgreSQL, MongoDB (replica set), or Amazon DocumentDB | +| Cloud | AWS (Lambda `nodejs22.x`, SQS, KMS, EventBridge Scheduler) | +| Deploy tool | `osls` (installed with `@friggframework/devtools`) | + +## 1. Run it on your database + +Frigg 2.0 stores data through Prisma behind repository interfaces. The same +integration code runs on PostgreSQL, MongoDB, and Amazon DocumentDB. + +```javascript +// backend/index.js +const Definition = { + name: 'my-app', + integrations: [HubSpotIntegration], + database: { postgres: { enable: true } }, // or mongoDB / documentDB + encryption: { fieldLevelEncryptionMethod: 'kms' }, // or 'aes' +}; +module.exports = { Definition }; +``` + +* `DATABASE_URL` holds the connection string. `frigg db:setup` checks it, + generates the Prisma client, and runs migrations (PostgreSQL) or `db push` + (MongoDB). +* Field-level encryption runs inside a Prisma client extension. Repositories + and your code see plain values. Credentials, mappings, password hashes, and + tokens are encrypted at rest with AWS KMS or AES. The `dev`, `test`, and + `local` stages skip encryption. +* PostgreSQL deployments get `/admin/db-migrate` endpoints to check and apply + schema migrations. `POST /admin/db-migrate/resolve` marks a failed migration + as applied or rolled back (#627). + +## 2. Operate it in production + +* **`frigg doctor`** checks a deployed CloudFormation stack. **`frigg repair`** + imports orphaned resources and reconciles drift. **`frigg generate-iam`** + writes a deployment IAM stack scoped to your app definition. `frigg deploy` + runs `doctor` after each deploy unless you pass `--skip-doctor`. +* **Stay under the 4 KB Lambda environment limit** (#625). Mark a variable + with `environment: { MY_SECRET: 'ssm' }` and Frigg stores it in SSM + Parameter Store and loads it at cold start. `frigg ssm push` uploads the + values, and `frigg deploy` runs it for you. `lambda.scopedEnvironment: true` + gives each function only the variables it uses. See + [ADR-027](../architecture-decisions/027-ssm-parameter-offload-and-env-scoping.md). +* **Structured JSON logs** (#654). Every record is one redacted JSON line with + `requestId`, `integrationId`, and OpenTelemetry `trace_id`/`span_id` when a + span is active. Use `this.logger` in integrations and API modules. Set + `logging: { level, retentionInDays }` in the app definition to control + `FRIGG_LOG_LEVEL` and CloudWatch retention. See [Logging](../guides/LOGGING.md). +* **OpenTelemetry and usage tracking** (#622). Deployed stages export nothing + until you set `telemetry.exporter` (`otlp`, `honeycomb`, `datadog`, or + `console`). Handler dispatch and API module requests are traced with no + code. Call `this.telemetry.span()` and `this.telemetry.count()` for your own + spans and counters. Declare `Definition.usage` on an integration and Frigg + keeps durable counters such as `records.synced` and `api.requests` in a + `UsageCounter` table. Reports and scripts read them through + `frigg.usage.getTotalsByDimension()` and `frigg.usage.getTimeSeries()`. See the + [telemetry guide](https://github.com/friggframework/frigg/blob/next/packages/core/telemetry/README.md). +* **Health endpoints**: `/health`, `/health/detailed` (includes an encryption + round trip), `/health/live`, and `/health/ready`. +* **Large dependency trees.** Set `lambda.keepNestedNodeModules: true` when a + package needs its own nested `node_modules` in the Lambda bundle (#645). + +## 3. Admin work inside your VPC + +**Admin scripts** (#517) run maintenance code in your deployed app, with the +same VPC, KMS, and database access as your integrations. Add +`adminScripts: [MyScript]` to the app definition and Frigg provisions the +router, executor Lambda, and queue. + +* A script extends `AdminScriptBase` and reads data through + `this.context.commands`. `this.context.instantiate(integrationId)` gives it a + live integration to call external APIs. +* `POST /admin/scripts/{name}` runs it in `sync` mode (inside the API call) or + `async` mode (queued, up to 15 minutes). Every run is stored as an + `AdminScriptExecution` record. +* `queueScript()` chains follow-up scripts. With + `admin: { enableScheduling: true }`, `PUT /admin/scripts/{name}/schedule` + creates an EventBridge schedule. + +**Reports** (#628) are admin operations whose output is the payload. A report +extends `ReportBase` and you register it with `reports: [MyReport]`. Set +`admin: { includeBuiltinReports: true }` to add the built-in `integrations` +report. + +* Run modes: `live` returns the result inline, `recorded` stores an execution + you poll, and `snapshot` stores a run in a named series for trends. +* `POST /api/v2/reports/{name}/run` starts a run. CSV, PDF, and ZIP output goes + to a private S3 bucket and comes back as a signed URL. +* Scripts, reports, and `/admin/db-migrate` all use the + `x-frigg-admin-api-key` header, checked against `ADMIN_API_KEY`. + +See [ADR-005](../architecture-decisions/005-admin-script-runner.md), +[ADR-010](../architecture-decisions/010-reporting-as-admin-operation.md), and +the [admin scripts README](https://github.com/friggframework/frigg/blob/next/packages/admin-scripts/README.md). + +## 4. Integrations that stay connected + +* **One refresh per expired token** (#636). When many requests on one module + get a 401 at once, they share a single refresh. A newer credential that + another Lambda already stored is adopted instead of refreshed again. +* **Grace retry on 401** (#623). Requesters retry a 401 up to three times, even + without a refresh token, before they invalidate the credential. +* **Refresh tokens are kept.** When a provider's refresh response omits + `refresh_token`, `OAuth2Requester` keeps the one it has. +* **Clear failure records.** When credentials fail, the integration records an + "Authentication Error" message with the module and status code, then moves + to `ERROR` (#634). A successful re-auth moves it back to `ENABLED`. + `FetchError` messages keep the sanitized cause (#659). +* **No lost writes.** `this.patchConfig(patch)` merges config atomically in the + database (#614). `updateProcessMetrics` writes counters atomically, so + concurrent workers no longer overwrite each other (#638). +* **Safe lifecycle.** A failing `ON_DELETE` handler no longer blocks uninstall + (#615). Re-authorizing reuses the existing integration instead of creating + a duplicate (#613). + +## 5. Build faster + +* **Integration extensions.** An API module can ship a webhook receiver, + signature check, and queue dispatch. You bind it with `Definition.extensions` + and write only the handler method. See the + [extensions guide](https://github.com/friggframework/frigg/blob/next/packages/core/integrations/EXTENSIONS.md). +* **Per-account webhooks.** Set `webhooks: true` on an integration Definition + and implement `onWebhook`. +* **One-time scheduled jobs.** `createSchedulerCommands()` schedules a future + event onto an integration's queue through EventBridge Scheduler. Use it for + webhook renewals and delayed work. +* **Final-attempt signal** (#653). Queue handlers receive + `delivery.isLastAttempt`, so you can close out a run before SQS moves the + message to the dead-letter queue. +* **`queryMappings`** (#651). Filter, sort, and page an integration's mappings + inside the database instead of loading every row. It requires the + `IntegrationMapping.mapping` field to be excluded from encryption. +* **`frigg auth`** tests an API module's OAuth2 or API-key flow locally and + saves the credentials: `frigg auth test`, `list`, `get`, and `delete`. + +## 6. Under the hood + +* **Hexagonal architecture.** Handlers call use cases. Use cases call + repositories. Repositories talk to the database and AWS. Each layer is + tested on its own, and each database has its own repository adapter. +* **oss-serverless.** `frigg build` and `frigg deploy` run + `osls --config infrastructure.js`. Your `infrastructure.js` calls + `createFriggInfrastructure()`, which builds the stack from your app + definition and discovers your VPC, subnets, and KMS keys. +* **AWS SDK v3** everywhere. See the + [SDK v3 and osls guide](../reference/aws-sdk-v3-osls-migration.md). +* **esbuild and a Prisma Lambda layer** keep function bundles small. The layer + ships only the Prisma runtime client. + +## 7. What's next + +These items are on the roadmap. They are not part of 2.0. + +* **Capabilities**: a typed statement of what an app, integration, or API + module can do, exposed to agents as MCP tools + ([ADR-020](../architecture-decisions/020-capabilities.md)). +* **Agent harness and integration templates**: a structured runtime for agents + that build integrations, and reusable integration starting points + ([ADR-025](../architecture-decisions/025-agent-harness.md), + [ADR-023](../architecture-decisions/023-integration-templates.md)). +* **Fenestra** as a spec format that capabilities can point to. +* **Multi-step authorization** and shared entities. + +See the [Roadmap](../roadmap/page-1.md) and the +[ADR index](../architecture-decisions/README.md). + +## Start or upgrade + +* **New app:** run `frigg init my-app`, then `frigg install ` and + `frigg start`. +* **Existing 1.x app:** follow [Migrating from 1.x to 2.0](../guides/migrating-to-2.0.md). + +{% hint style="info" %} +If `frigg init` fails with "Backend template not found", upgrade +`@friggframework/devtools` to the latest `next` release and run it again. +{% endhint %} From 2745d8c647ba47549274b7eb1a7db3663185a693 Mon Sep 17 00:00:00 2001 From: Sean Matthews Date: Thu, 1 Oct 2026 16:09:11 -0400 Subject: [PATCH 2/8] docs: add the 1.x to 2.0 migration guide Rewrite the July draft against the code on next. The database engine comes from Definition.database or DB_TYPE, integrations move from Config/Options to static Definition, and data moves by a fresh database or a scripted copy. Drop the @friggframework/ui renames that never shipped and move the extension route change to a section for 2.0 prerelease adopters, next to the retired reporting key. Install commands use ^2.0.1. Co-Authored-By: Claude Opus 5.5 --- docs/guides/migrating-to-2.0.md | 264 ++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 docs/guides/migrating-to-2.0.md diff --git a/docs/guides/migrating-to-2.0.md b/docs/guides/migrating-to-2.0.md new file mode 100644 index 000000000..0188d8649 --- /dev/null +++ b/docs/guides/migrating-to-2.0.md @@ -0,0 +1,264 @@ +--- +description: >- + Upgrade a Frigg 1.x app to 2.0: Prisma instead of Mongoose, an explicit + database choice, Definition-based integrations, Node.js 22, and osls. +--- + +# Migrating from 1.x to 2.0 + +This guide moves a Frigg 1.x app to 2.0. Read it once before you start. Do the +work on a branch, and test against a copy of your production data. + +Frigg 2.0.1 is the first stable 2.x release. Install `^2.0.1`, never +`^2.0.0`: `@friggframework/core@2.0.0` is a deprecated 2024 build. + +For what 2.0 adds, see [What's New in Frigg 2.0](../getting-started/whats-new-in-2.0.md). + +{% hint style="danger" %} +Back up your database first. 2.0 uses a new schema and new collection names, +so plan the data move as a migration, not an in-place upgrade. +{% endhint %} + +## What changes + +| Area | 1.x | 2.0 | +| --- | --- | --- | +| Data access | Mongoose models exported from core | Prisma, behind repositories and `createFriggCommands()` | +| Database | MongoDB via `MONGO_URI` | PostgreSQL, MongoDB, or DocumentDB via `DATABASE_URL` plus an explicit engine | +| Users | `IndividualUser` and `OrganizationUser` discriminators | One `User` model with a `type` field | +| Integration class | `static Config`, `static Options`, `static modules` | One `static Definition` and `this.events` | +| Infrastructure | Hand-written `serverless.yml` | `infrastructure.js` generated from the app definition | +| Deploy tool | Serverless Framework | `osls` (oss-serverless), run by `frigg deploy` | +| AWS SDK | v2 (`aws-sdk`) | v3 (`@aws-sdk/client-*`) | +| Runtime | Node.js 18 | Node.js 22 (`nodejs22.x` on Lambda) | + +## Step 1: Upgrade Node.js + +Install Node.js 22 or later and npm 10 or later, locally and in CI +(`nvm install 22`). + +## Step 2: Start from a 2.0 backend + +The simplest path is a fresh 2.0 backend that you move your integrations into. + +```bash +npm install -g @friggframework/devtools@^2.0.1 +frigg init my-app +``` + +This writes `backend/index.js` (your app definition) and +`backend/infrastructure.js`. You do not edit `infrastructure.js`. It contains: + +```javascript +const { createFriggInfrastructure } = require('@friggframework/devtools'); +module.exports = createFriggInfrastructure(); +``` + +{% hint style="info" %} +If `frigg init` fails with "Backend template not found", upgrade +`@friggframework/devtools` to the latest 2.x release and run it again. +{% endhint %} + +To upgrade in place instead, install the 2.0 packages and the Prisma peers +(core lists them as optional peer dependencies, so install them yourself): + +```bash +npm install @friggframework/core@^2.0.1 @friggframework/devtools@^2.0.1 +npm install @prisma/client prisma +``` + +Then add the `infrastructure.js` above and delete your 1.x `serverless.yml`. +Move any custom resources from it into the app definition (`vpc`, +`encryption`, `ssm`, `environment`). + +Update each `@friggframework/api-module-*` package to a version whose +`package.json` depends on `@friggframework/core` 2.x. + +## Step 3: Choose the database + +Frigg 2.0 does not guess the engine from the URL. Enable exactly one database +in the app definition: + +```javascript +// backend/index.js +const Definition = { + name: 'my-app', + integrations: [HubSpotIntegration], + database: { mongoDB: { enable: true } }, // or postgres / documentDB + encryption: { fieldLevelEncryptionMethod: 'kms' }, // or 'aes' +}; +module.exports = { Definition }; +``` + +A `DB_TYPE` environment variable (`postgresql`, `mongodb`, or `documentdb`) +overrides the app definition. Frigg throws if it finds neither. + +Replace `MONGO_URI` with `DATABASE_URL`. It is only the connection string. +Prisma needs MongoDB to run as a replica set. + +```bash +DATABASE_URL="postgresql://user:password@localhost:5432/frigg" +DATABASE_URL="mongodb://localhost:27017/frigg?replicaSet=rs0" +``` + +Keep your encryption keys. 2.0 reads the same variables as 1.x: `KMS_KEY_ARN` +for KMS, or `AES_KEY_ID` and `AES_KEY` for AES. The `dev`, `test`, and `local` +stages skip encryption. + +## Step 4: Create the schema + +```bash +frigg db:setup +``` + +This checks `DATABASE_URL`, generates the Prisma client, and runs migrations +(PostgreSQL) or `db push` (MongoDB and DocumentDB). + +## Step 5: Port your integration classes + +Replace `Config`, `Options`, and `modules` with one `Definition`. Map events +to handler methods in the constructor. + +{% code title="1.x" %} +```javascript +class HubSpotIntegration extends IntegrationBase { + static Config = { name: 'hubspot', version: '1.0.0', events: ['SEARCH_DEALS'] }; + static Options = new Options({ module: HubSpotModule, display: { /* ... */ } }); + static modules = { hubspot: HubSpotModule }; + + async receiveNotification(notifier, event, object) { + if (event === 'SEARCH_DEALS') return this.target.api.searchDeals(object); + } +} +``` +{% endcode %} + +{% code title="2.0" %} +```javascript +const hubspot = require('@friggframework/api-module-hubspot'); + +class HubSpotIntegration extends IntegrationBase { + static Definition = { + name: 'hubspot', + version: '1.0.0', + display: { label: 'HubSpot', description: 'CRM sync', category: 'CRM' }, + modules: { hubspot: { definition: hubspot.Definition } }, + routes: [{ path: '/deals/search', method: 'POST', event: 'SEARCH_DEALS' }], + }; + + constructor(params) { + super(params); + this.events = { SEARCH_DEALS: { handler: this.searchDeals } }; + } + + async searchDeals({ data }) { + return this.hubspot.api.searchDeals(data); + } +} +``` +{% endcode %} + +* Call a module through its key: `this.hubspot.api`, not `this.target.api`. +* Replace `this.record.save()` and `markModified()`. Use + `this.patchConfig(patch)` to merge keys and `this.updateConfig(config)` to + replace the config. Both write to the database and update `this.config`. +* The default `onCreate()` validates config and sets `ENABLED` or + `NEEDS_CONFIG`. If you override it, call `super.onCreate()`. +* Use `this.logger` instead of `console.log`. See + [Logging in an Integration](./LOGGING-IN-INTEGRATIONS.md). + +## Step 6: Replace direct database access + +Core no longer exports `mongoose`, the Mongoose models (`UserModel`, +`IndividualUser`, `OrganizationUser`, `IntegrationModel`, `Token`, `State`), +`connectToDatabase`, `createFriggBackend`, `ModuleManager`, `Auther`, or +`IntegrationFactory`. + +* Use `createFriggCommands({ integrationClass })` for users, credentials, + entities, integrations, and processes. +* Check `user.type` (`INDIVIDUAL` or `ORGANIZATION`) instead of the + discriminator class. +* Use `findIndividualUsersByOrganizationId` to list the members of an + organization user. +* Handle the `IN_CREATION` and `IN_DELETION` integration statuses anywhere you + filter or display status. + +## Step 7: Move AWS SDK v2 calls to v3 + +Only custom code that calls AWS directly needs this. + +```javascript +// 1.x: await new AWS.SQS().sendMessage(params).promise(); +const { SQSClient, SendMessageCommand } = require('@aws-sdk/client-sqs'); +await new SQSClient({}).send(new SendMessageCommand(params)); +``` + +See [AWS SDK v3 and oss-serverless](../reference/aws-sdk-v3-osls-migration.md). + +## Step 8: Move your data + +There is no automated 1.x data migration. The tool is designed in +[ADR-004](../architecture-decisions/004-migration-tool-design.md) but has not +shipped. Pick one path: + +* **Fresh database.** Deploy 2.0 against an empty database and ask users to + reconnect their accounts. This is the lowest-risk path. +* **Scripted copy.** Write a one-off script that reads the 1.x collections and + writes the 2.0 models (`User`, `Credential`, `Entity`, `Integration`, + `IntegrationMapping`). The schemas are in `packages/core/prisma-mongodb/` and + `packages/core/prisma-postgresql/`. Check on staging that migrated + credentials decrypt and refresh before you cut over. + +## Step 9: Deploy and verify + +```bash +frigg build --production +frigg deploy --stage dev +``` + +`frigg deploy` runs `osls` and then `frigg doctor`. After the deploy: + +* Call `GET /health/detailed` and confirm the database and encryption checks + pass. +* On PostgreSQL, call `GET /admin/db-migrate/status` with the + `x-frigg-admin-api-key` header and apply pending migrations with + `POST /admin/db-migrate`. +* If the deploy fails on the 4 KB Lambda environment limit, move large values + to SSM with `environment: { KEY: 'ssm' }`. See + [ADR-027](../architecture-decisions/027-ssm-parameter-offload-and-env-scoping.md). +* Run `frigg repair ` if `doctor` reports drift. + +## If you ran a 2.0 prerelease + +These changes affect apps built on earlier `2.0.0-next` releases. 1.x apps can +skip them. + +* **Extension routes are namespaced.** Routes mount at + `/api/{integration}-integration/{bindingKey}{path}`. Re-point every provider + webhook registered at the old path. Extension receivers default to + `useDatabase: false`. +* **The reporting key is retired.** `REPORTING_API_KEY` and + `x-frigg-reporting-api-key` no longer work. Set `ADMIN_API_KEY` and send + `x-frigg-admin-api-key`. +* **The integrations report moved.** `GET /api/v2/reports/integrations` is + deprecated and is only deployed when you register `reports` or set + `admin: { includeBuiltinReports: true }`. Call + `POST /api/v2/reports/integrations/run` with `{ "mode": "live" }` instead. +* **Process updates are atomic.** `updateProcessMetrics` and the context + updates in `updateProcessState` no longer read, modify, and write the whole + record. No code change is needed. + +## Checklist + +* [ ] Node.js 22+ and npm 10+ locally and in CI +* [ ] `@friggframework/core` and `@friggframework/devtools` on `^2.0.1`, plus `prisma` and `@prisma/client` +* [ ] API modules on versions that depend on core 2.x +* [ ] `infrastructure.js` in place; `serverless.yml` removed +* [ ] `database..enable` (or `DB_TYPE`) set, and `DATABASE_URL` replaces `MONGO_URI` +* [ ] Encryption keys carried over +* [ ] `frigg db:setup` run +* [ ] Integrations use `static Definition`, `this.events`, and `this..api` +* [ ] No imports of removed core exports; `user.type` checks in place +* [ ] Custom AWS calls on SDK v3 +* [ ] Data plan chosen and tested on staging +* [ ] Deployed; `frigg doctor` clean; `/health/detailed` passes From c34bfb01232bcef49d06342e09676de1d001f23f Mon Sep 17 00:00:00 2001 From: Sean Matthews Date: Thu, 1 Oct 2026 16:09:11 -0400 Subject: [PATCH 3/8] docs: point What's New at the 2.0.1 release 2.0.1 is the first stable 2.x release. Tell readers to install ^2.0.1, because ^2.0.0 can resolve to a deprecated 2024 build of core. Co-Authored-By: Claude Opus 5.5 --- docs/getting-started/whats-new-in-2.0.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/whats-new-in-2.0.md b/docs/getting-started/whats-new-in-2.0.md index a42a81a0e..ce19b977d 100644 --- a/docs/getting-started/whats-new-in-2.0.md +++ b/docs/getting-started/whats-new-in-2.0.md @@ -12,6 +12,10 @@ PostgreSQL, MongoDB, or DocumentDB. You deploy, check, and repair it with one CLI. You run admin scripts and reports inside your own VPC. Your integrations refresh tokens, retry, and record their state without losing writes. +Frigg 2.0.1 is the first stable 2.x release. Install `^2.0.1`. Do not use +`^2.0.0`, which can resolve to a deprecated 2024 build of +`@friggframework/core`. + {% hint style="warning" %} 2.0 has breaking changes. If you run a 1.x app, read [Migrating from 1.x to 2.0](../guides/migrating-to-2.0.md) before you upgrade. @@ -212,11 +216,12 @@ See the [Roadmap](../roadmap/page-1.md) and the ## Start or upgrade -* **New app:** run `frigg init my-app`, then `frigg install ` and - `frigg start`. +* **New app:** install the CLI with + `npm install -g @friggframework/devtools@^2.0.1`. Then run + `frigg init my-app`, `frigg install `, and `frigg start`. * **Existing 1.x app:** follow [Migrating from 1.x to 2.0](../guides/migrating-to-2.0.md). {% hint style="info" %} If `frigg init` fails with "Backend template not found", upgrade -`@friggframework/devtools` to the latest `next` release and run it again. +`@friggframework/devtools` to the latest 2.x release and run it again. {% endhint %} From 3a3f81d53ce425efd8b027e90de5d8832a3146a6 Mon Sep 17 00:00:00 2001 From: Sean Matthews Date: Thu, 1 Oct 2026 16:09:17 -0400 Subject: [PATCH 4/8] docs: add the 2.0 pages to the table of contents Co-Authored-By: Claude Opus 5.5 --- docs/SUMMARY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 7de5f1dd0..550d76cdc 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -1,6 +1,7 @@ # Table of contents * [🥳 Welcome to Frigg!](README.md) +* [What's New in Frigg 2.0](getting-started/whats-new-in-2.0.md) ## 👩‍💻 Tutorials (Learning) @@ -18,6 +19,7 @@ ## ✅ How-To Guides (Goals) * [Cooking with Frigg](guides/cooking-with-frigg.md) +* [Migrating from 1.x to 2.0](guides/migrating-to-2.0.md) * [Logging](guides/LOGGING.md) * [Logging in an Integration](guides/LOGGING-IN-INTEGRATIONS.md) From 99cd0c3288b531d9a23f3931eefd12215af1e7d3 Mon Sep 17 00:00:00 2001 From: Sean Matthews Date: Thu, 1 Oct 2026 16:10:41 -0400 Subject: [PATCH 5/8] docs: pin Prisma to core's peer range in the migration guide npm's latest tag for prisma currently points at an 8.x release candidate, outside @friggframework/core's ^6.19.3 peer range. Co-Authored-By: Claude Opus 5.5 --- docs/guides/migrating-to-2.0.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/guides/migrating-to-2.0.md b/docs/guides/migrating-to-2.0.md index 0188d8649..d1e24d654 100644 --- a/docs/guides/migrating-to-2.0.md +++ b/docs/guides/migrating-to-2.0.md @@ -63,8 +63,9 @@ To upgrade in place instead, install the 2.0 packages and the Prisma peers (core lists them as optional peer dependencies, so install them yourself): ```bash -npm install @friggframework/core@^2.0.1 @friggframework/devtools@^2.0.1 -npm install @prisma/client prisma +npm install @friggframework/core@^2.0.1 +npm install -D @friggframework/devtools@^2.0.1 +npm install @prisma/client@^6.19.3 prisma@^6.19.3 ``` Then add the `infrastructure.js` above and delete your 1.x `serverless.yml`. From e89afebb5748afc32cca175a8d0992ef1580cd86 Mon Sep 17 00:00:00 2001 From: Sean Matthews Date: Fri, 2 Oct 2026 16:03:29 -0400 Subject: [PATCH 6/8] docs: describe fail-closed encryption in the 2.0 notes and migration guide Co-Authored-By: Claude Opus 5.5 --- docs/getting-started/whats-new-in-2.0.md | 7 +++++++ docs/guides/migrating-to-2.0.md | 22 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/whats-new-in-2.0.md b/docs/getting-started/whats-new-in-2.0.md index ce19b977d..b48298d2a 100644 --- a/docs/getting-started/whats-new-in-2.0.md +++ b/docs/getting-started/whats-new-in-2.0.md @@ -39,6 +39,13 @@ Frigg 2.0.1 is the first stable 2.x release. Install `^2.0.1`. Do not use * **New integration statuses.** Integrations start in `IN_CREATION` and pass through `IN_DELETION` on teardown. Code that filters on status must handle both. +* **Deployed stages always encrypt.** A function running in AWS Lambda + encrypts sensitive fields on every stage, `dev` included, and refuses to + start without a key (`EncryptionConfigurationError`). Set + `encryption: { fieldLevelEncryptionMethod: 'kms' }`, or provide `AES_KEY_ID` + and `AES_KEY`. Only local runs (`frigg start`, tests) skip encryption. To + store a deployed stage in plaintext on purpose, set + `fieldLevelEncryptionMethod: 'none'`. * **Node.js 22, AWS SDK v3, and osls.** Lambda functions run on `nodejs22.x`. Deploys use oss-serverless (`osls`) instead of the Serverless Framework. diff --git a/docs/guides/migrating-to-2.0.md b/docs/guides/migrating-to-2.0.md index d1e24d654..39b76584e 100644 --- a/docs/guides/migrating-to-2.0.md +++ b/docs/guides/migrating-to-2.0.md @@ -103,8 +103,20 @@ DATABASE_URL="mongodb://localhost:27017/frigg?replicaSet=rs0" ``` Keep your encryption keys. 2.0 reads the same variables as 1.x: `KMS_KEY_ARN` -for KMS, or `AES_KEY_ID` and `AES_KEY` for AES. The `dev`, `test`, and `local` -stages skip encryption. +for KMS, or `AES_KEY_ID` and `AES_KEY` for AES. + +{% hint style="warning" %} +**2.0 fails closed.** In 1.x the `dev`, `test`, and `local` stages skipped +encryption, and a stage without a key wrote plaintext. In 2.0 every deployed +stage, `dev` included, encrypts, and a deployed function with no key refuses to +start with `EncryptionConfigurationError`. Set +`encryption: { fieldLevelEncryptionMethod: 'kms' }` and Frigg creates or +discovers the key on every stage. Rows already stored in plaintext stay +readable and are encrypted the next time they are written. Only local runs +(`frigg start`, tests) still skip encryption on `dev`, `test`, and `local`. To +keep a deployed stage in plaintext on purpose, set +`fieldLevelEncryptionMethod: 'none'`; Frigg logs a warning on every cold start. +{% endhint %} ## Step 4: Create the schema @@ -245,6 +257,10 @@ skip them. deprecated and is only deployed when you register `reports` or set `admin: { includeBuiltinReports: true }`. Call `POST /api/v2/reports/integrations/run` with `{ "mode": "live" }` instead. +* **Deployed `dev` stages now encrypt.** Prereleases skipped encryption on + `dev`, `test`, and `local` even when deployed. A deployed stage without a key + now fails at startup; add `encryption: { fieldLevelEncryptionMethod: 'kms' }` + and redeploy. `BYPASS_ENCRYPTION_STAGE` is no longer read. * **Process updates are atomic.** `updateProcessMetrics` and the context updates in `updateProcessState` no longer read, modify, and write the whole record. No code change is needed. @@ -256,7 +272,7 @@ skip them. * [ ] API modules on versions that depend on core 2.x * [ ] `infrastructure.js` in place; `serverless.yml` removed * [ ] `database..enable` (or `DB_TYPE`) set, and `DATABASE_URL` replaces `MONGO_URI` -* [ ] Encryption keys carried over +* [ ] Encryption configured for every deployed stage (`kms`, AES keys, or an explicit `none`) * [ ] `frigg db:setup` run * [ ] Integrations use `static Definition`, `this.events`, and `this..api` * [ ] No imports of removed core exports; `user.type` checks in place From 4b5687713ae0628134e5707a4a2f9abbdc6c058a Mon Sep 17 00:00:00 2001 From: Sean Matthews Date: Thu, 22 Oct 2026 16:09:33 -0400 Subject: [PATCH 7/8] docs: reframe the 2.0 notes as the integration backend for people and their agents Rewrite What's New around five pillars: three ways to define an integration (code, a validated document, a catalogue entry), connections agents use without seeing a credential (Management API v2, adopter JWTs, memberships, entity scopes, the locked-down proxy), the product and operations profiles, production defaults, and Netlify as a second host. Add the new breaking changes and requirements. Update the migration guide for Node 22.13+/24 and osls 4, frigg validate, module credential encryption, and a Management API v2 step that keeps v1 clients working and links the v1 to v2 table. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013b2UUFaQ5Lk6TFPqzRr1j7 --- docs/getting-started/whats-new-in-2.0.md | 560 +++++++++++++++-------- docs/guides/migrating-to-2.0.md | 84 +++- 2 files changed, 440 insertions(+), 204 deletions(-) diff --git a/docs/getting-started/whats-new-in-2.0.md b/docs/getting-started/whats-new-in-2.0.md index b48298d2a..0744f6a7c 100644 --- a/docs/getting-started/whats-new-in-2.0.md +++ b/docs/getting-started/whats-new-in-2.0.md @@ -1,16 +1,17 @@ --- description: >- - Frigg 2.0 is the production foundation: your choice of database, real - operations tooling, admin work inside your VPC, and integrations that stay - connected. + Frigg 2.0 is the integration backend for people and their agents: three ways + to define an integration on one runtime, connections that agents use without + seeing a credential, two app profiles, production defaults, and more than one + host. --- # What's New in Frigg 2.0 -Frigg 2.0 is the production foundation for native integrations. You run it on -PostgreSQL, MongoDB, or DocumentDB. You deploy, check, and repair it with one -CLI. You run admin scripts and reports inside your own VPC. Your integrations -refresh tokens, retry, and record their state without losing writes. +**Frigg 2.0 is the integration backend for people and their agents.** Each +person and each organization connects their own accounts once. People, your +product, and the agents working for them then use those connections through +one versioned API, and nobody outside Frigg ever holds the credential. Frigg 2.0.1 is the first stable 2.x release. Install `^2.0.1`. Do not use `^2.0.0`, which can resolve to a deprecated 2024 build of @@ -21,202 +22,379 @@ Frigg 2.0.1 is the first stable 2.x release. Install `^2.0.1`. Do not use [Migrating from 1.x to 2.0](../guides/migrating-to-2.0.md) before you upgrade. {% endhint %} +2.0 is built on five ideas: + +1. [Three ways to define an integration, one runtime](#1-three-ways-to-define-an-integration-one-runtime) +2. [Connections agents can use without seeing a credential](#2-connections-agents-can-use-without-seeing-a-credential) +3. [Two app profiles: product and operations](#3-two-app-profiles-product-and-operations) +4. [Production by default](#4-production-by-default) +5. [More than one host](#5-more-than-one-host) + +{% hint style="info" %} +**We run it ourselves.** Left Hook, the company that maintains Frigg, runs +both profiles on 2.0. One is an internal agent tool server with hundreds of +tools, where every person's connections live in Frigg instead of in shared +environment variables (the operations profile, on Netlify). The other is a +product where customers' integrations are built from a catalogue or by chat +and validated by Frigg (the product profile, on AWS). 2.0.1 ships when both +run on a published `next` prerelease. +{% endhint %} + +## 1. Three ways to define an integration, one runtime + +An integration can be code, a validated JSON document, or an entry in your +app's catalogue. All three install, list, route, run, and log the same way. + +### In code + +A class that extends `IntegrationBase` with a `static Definition` works as it +did in every 2.0 prerelease. 2.0.1 adds `defineIntegration`, which builds that +class from one object +([ADR-054](https://github.com/friggframework/frigg/pull/669)): + +```javascript +const { defineApp, defineIntegration, use } = require('@friggframework/core'); + +const DealAlerts = defineIntegration({ + name: 'deal-alerts', + version: '1.0.0', + display: { label: 'Deal alerts in Slack', category: 'Notifications' }, + modules: { crm: use(hubspot), chat: use(slack) }, + on: { + 'crm.deal.changed': async ({ deal }, { modules: { chat }, config }) => { + /* post to config.channel */ + }, + }, + triggers: { digest: { every: '1d', handler: sendDigest } }, // ADR-055 +}); + +module.exports = defineApp({ name: 'my-app', integrations: [DealAlerts] }); +``` + +`defineIntegration` returns an `IntegrationBase` subclass, so everything that +reads `Definition` today works with it. + +### As a document + +An integration can also be a JSON document +([ADR-063](https://github.com/friggframework/frigg/pull/692)). It declares +modules, settings, syncs, and workflows. It has no HTTP or script step: +every step is either a built-in step or a capability +(`..`) that the module's capability manifest declares. + +```json +{ + "schemaVersion": "0.1", + "id": "pipedrive-hubspot", + "version": "1.0.0", + "display": { "label": "Pipedrive ⇄ HubSpot people", "category": "CRM" }, + "modules": { + "pipedrive": { "module": "pipedrive", "version": "^2.1" }, + "hubspot": { "module": "hubspot", "version": "^3" } + }, + "syncs": [{ + "id": "people", + "source": "pipedrive.person", + "target": "hubspot.contact", + "direction": "two_way", + "identity": { "match": [{ "source": "emails[0].value", "target": "properties.email", "transform": "lowercase", "key": "email" }] }, + "mapping": [{ "source": "first_name", "target": "properties.firstname", "transform": "trim" }], + "backfill": { "trigger": "userAction", "pageSize": 100 }, + "ongoing": { "mode": "webhook" }, + "default": "on" + }] +} +``` + +This is abridged from `examples/pipedrive-hubspot.definition.json`. That +example also subscribes the webhook in `lifecycle.onInstall`. + +* **Validated twice.** Frigg checks a document against the JSON Schema and + then checks its references. Each capability must exist in a manifest, and + each manifest method must exist on the real API module. Run + `npm run validate:definitions --workspace @friggframework/core` in CI. +* **Published as immutable versions.** `POST /admin/integrations/versions` + publishes a version. A published version never changes. Frigg refuses a + different document at the same version, and a version whose semver bump is + smaller than its changes. Each installation pins a version and a checksum. + `GET`/`POST /api/integrations/:integrationId/upgrade` plans and applies an + upgrade. +* **Installed like code.** A published id is an integration type. Install it + with `POST /api/integrations` and `config: { type: '' }`. It lists, + routes, and logs under its own name. When code and a document share a name, + code wins. +* **Code can embed a document.** + `extendIntegrationClass(MyIntegration, { definition, manifests })` adds a + document's syncs and workflows to a code class. A `code` step with + `ref: "#methodName"` calls a method on that class. + +### From a catalogue + +`GET /api/integration-definitions` lists the latest published version of each +definition your app offers. Workspaces can publish their own definitions with +`POST /api/integration-definitions`. Other workspaces never see them. + +### What a definition can do + +* **Workflows** run on a webhook, a schedule, a user action, or a synced + record. Steps include `map`, `branch`, `foreach`, `links.find`, and + `links.upsert`. `delay` (`for: '2h'`) pauses a run, and `approval` waits for + a person. A person approves or rejects with + `POST /api/integrations/:integrationId/runs/:runId/decision`. Every run is + stored and listed at `GET /api/integrations/:integrationId/runs`. +* **A sync engine.** A sync has a source, a target, a direction (one-way or + `two_way`), identity matching, a field mapping, a backfill, and ongoing + updates by webhook or by polling. +* **Recurring triggers** ([ADR-055](https://github.com/friggframework/frigg/pull/669)). + `Definition.triggers` takes `every: '15m'` or a five-field `cron` with a + `timezone`. With `configurable: true`, each installation picks its own + interval. One shared tick runs every trigger, so Frigg does not create a + schedule per installation. +* **Previews and dry runs.** `previewWorkflow()` runs a workflow against + recorded calls and returns what it would send, without calling any API. + `previewSurface()` renders an integration's UI as Slack blocks, a HubSpot + card, or HTML. + +## 2. Connections agents can use without seeing a credential + +### Management API v2 + +([ADR-053](https://github.com/friggframework/frigg/pull/668), #671) + +* Every route lives under `/api/v2/*`. Errors share one shape, and every + response carries `Frigg-API-Version: 2`. A resource that does not exist and + one owned by someone else return the same `404`. +* `GET /api/meta` needs no authentication. It returns the API versions, their + status, their OpenAPI documents, and feature flags such as `entityProxy`. +* Entities never return credential fields. `GET /api/v2/credentials` returns + credentials with their secrets masked. +* `GET`/`POST /api/v2/authorize` handles single-step and multi-step + authorization. +* **v1 still works, but it is frozen and deprecated.** Every v1 response + carries `Deprecation` and `Link` headers, and each v1 call is logged and + counted. v1 will not be removed before core 3.0. When nothing uses v1, set + `managementApi: { v1: false }`. See the + [API reference](../reference/api-reference.md) for the v1 to v2 table. + +### Who is calling + +* **Adopter tokens** (#678). Your product or your agent host signs a JWT and + sends it as `Authorization: Bearer `. Turn it on with + `user.authModes.adopterJwt: true` and `user.jwtConfig` (`jwksUri` or + `secret`, `issuer`, `audience`). Frigg finds or creates the user and the + organization named in the token's claims (`sub` and `org_id` by default). +* **Organization memberships** (#681). A person can belong to several + organizations. Each request acts in one active organization: the token's + `org_id` claim or the `x-frigg-apporgid` header. +* **Organization roles** (#683). Members can use the organization's + connections. Creating, changing, or deleting them needs a role listed in + `user.orgAdminRoles` (default `['ADMIN']`). +* **Bearer tokens resolve to their owner** (#691, merged). A Frigg bearer + token now acts as the user who owns it. + +### Whose connection it is + +([ADR-056](https://github.com/friggframework/frigg/pull/670), #682) + +* Every entity has a scope: `individual` (one person), `organization` (shared + by the organization), or `global` (shared by the whole app). +* The default follows `user.primary`: `organization` when it is + `'organization'`, otherwise `individual`. Existing apps keep their behavior. + You can set a scope per module binding, per integration + (`Definition.ownership`), per entity type, or for the whole app + (`appDefinition.ownership`), and you can let the caller choose from an + `allow` list. +* Lists filter by `?scope=` and `?owner=me|org`, and each item says what the + caller may do with it (`access`). +* Credentials are scoped through their entities (#684). Frigg refuses to + delete a credential that another organization still uses. +* **App-level entity registry** (ADR-059, #679). + `appDefinition.entities: { : { definition, scope, proxy } }` + lets people connect a module that no integration uses yet. For example, a + person can connect an account only so an agent can use it. +* **Attach and detach** (#689, merged). + `addEntitiesToIntegration` and `removeEntitiesFromIntegration` change the + connections on an existing integration. + +### The entity proxy + +([ADR-052](https://github.com/friggframework/frigg/pull/668), #671, #685, #690) + +`POST /api/v2/entities/:entityId/proxy` calls the connected account's API +with the stored credential. The caller sends a method and a path. Frigg adds +the credential, refreshes the token if needed, and returns the response. The +caller never sees the token, which is what lets you hand a connection to an +agent. + +```text +POST /api/v2/entities/7/proxy +{ "method": "GET", "path": "/crm/v3/objects/contacts", "query": { "limit": 10 } } +``` + +* **Off by default and denied by default.** Turn it on with + `managementApi.proxy.enable: true`. Each API module lists the methods and + paths it allows in `proxy.allow`, and the app can narrow that list per + module. +* **The host always comes from the module.** Frigg refuses absolute URLs, + `..`, encoded separators, and credential headers. A module that spans + several hosts declares named `proxy.baseUrls`, and the request picks one by + name (`"base": "docs"`), never by URL (#690). +* **Scopes.** `managementApi.proxy.scopes` (default `['individual']`) decides + whose connections the proxy may use. Add `'organization'` to allow shared + ones. `'global'` is never allowed (#685). +* Every call is audited as `frigg.api.proxy.request`. Upstream 429s keep + `Retry-After`. + +## 3. Two app profiles: product and operations + +([ADR-056](https://github.com/friggframework/frigg/pull/670)) + +| | Product | Operations | +| --- | --- | --- | +| Who adopts | A software company building integrations for its customers | A company, a household, or one person automating its own work | +| Tenancy | Many customer organizations in one shared deployment, each isolated | One organization | +| Who connects accounts | Each customer and each of their users | Each member connects their own, and the organization shares some | +| Typical auth | Adopter JWT from the product | Staff login, or adopter JWT from an internal tool | + +Both profiles use the same runtime and the same Management API. The difference +is configuration: the user model, the default entity scope, and which scopes +the proxy allows. + +## 4. Production by default + +* **Encryption fails closed** (#663). Every deployed stage encrypts sensitive + fields, `dev` included. A deployed function with no key refuses to start + with `EncryptionConfigurationError`. Set + `encryption: { fieldLevelEncryptionMethod: 'kms' }`, or provide `AES_KEY_ID` + and `AES_KEY`. Only local runs (`frigg start`, tests) skip encryption. To + store a deployed stage in plaintext on purpose, set + `fieldLevelEncryptionMethod: 'none'`. Frigg then logs a warning on every + cold start. +* **Module credentials are encrypted too** (#680). Fields an API module lists + in `encryption.credentialFields` are now encrypted. Before 2.0.1 they were + silently stored in plaintext. Reads decrypt only values that have the exact + ciphertext shape, so stored URLs that contain colons are no longer mistaken + for ciphertext. +* **A scaffold that works and costs almost nothing idle** (#639). `frigg init` + writes a backend that installs, starts, and deploys. It has no VPC and no + NAT gateway, uses your own PostgreSQL through `DATABASE_URL`, and encrypts + with KMS. Idle, it costs about $1 a month for the KMS key. +* **`frigg validate`** (#666, + [ADR-051](https://github.com/friggframework/frigg/pull/666)). It checks your + app definition against a schema that now matches what the runtime reads. It + reports each error with a JSON pointer and a hint. `frigg build` and + `frigg deploy` run it first and stop on errors. +* **osls 4** ([ADR-050](https://github.com/friggframework/frigg/pull/664)). + Deploys move to oss-serverless 4. The CLI needs Node.js 22.13 or later, or + Node.js 24. Lambda functions stay on `nodejs22.x`. +* **Rate limits that wait instead of fail** (ADR-049, #656–#658, if merged). + An API module declares `static rateLimit`. The requester honours + `Retry-After`. A queue message that hits a long limit goes back on the queue + at its retry time instead of failing. When the wait is too long, the + integration records a warning the user can see. + +## 5. More than one host + +* **AWS by default.** Lambda, SQS, KMS, and EventBridge Scheduler, generated + from your app definition. +* **Netlify as a second provider** + ([ADR-028](https://github.com/friggframework/frigg/pull/644), #686). Set + `provider: 'netlify'` in the app definition. `frigg build`, `frigg deploy`, + and `frigg start` then use Netlify Functions, background functions for + queues, and a scheduled function for recurring work. `frigg doctor`, + `repair`, and `generate-iam` stay AWS-only. + +## Under the hood + +* **Your database.** A Prisma data layer behind repositories runs the same + code on PostgreSQL, MongoDB, and Amazon DocumentDB. Choose the engine with + `database..enable` or `DB_TYPE`. `frigg db:setup` generates the + client and applies the schema. +* **Operations tooling.** `frigg doctor`, `frigg repair`, and + `frigg generate-iam`. SSM offload for large environments (#625). Structured + JSON logs (#654). Opt-in OpenTelemetry and usage counters (#622). Health + endpoints at `/health`, `/health/detailed`, `/health/live`, and + `/health/ready`. +* **Admin work inside your VPC.** Admin scripts (#517) and reports with + `live`, `recorded`, and `snapshot` run modes (#628), both behind + `x-frigg-admin-api-key`. +* **Connections that stay up.** Concurrent 401s share one token refresh + (#636). A 401 gets a grace retry (#623). The existing `refresh_token` is kept + when a provider omits it. Integrations record why a credential failed (#634). + Config and process metrics update atomically (#614, #638). +* **Less plumbing.** Integration extensions (`Definition.extensions`), + per-account webhooks, one-time jobs with `createSchedulerCommands()`, + `delivery.isLastAttempt` (#653), `queryMappings` (#651), and `frigg auth` + for testing a module's auth locally. +* **Hexagonal architecture.** Handlers call use cases, and use cases call + repositories. AWS SDK v3, esbuild bundles, and a Prisma Lambda layer. + ## Breaking changes at a glance * **Mongoose is gone.** Data access runs on Prisma. Core no longer exports `mongoose`, the Mongoose models, `connectToDatabase`, `createFriggBackend`, `ModuleManager`, `Auther`, or `IntegrationFactory`. * **You pick the database explicitly.** Set `database.postgres.enable`, - `database.mongoDB.enable`, or `database.documentDB.enable` in your app - definition, or set `DB_TYPE`. `DATABASE_URL` is the connection string only. - Frigg throws at startup if neither tells it the engine. -* **Integration classes use `static Definition`.** The 1.x `static Config`, - `static Options`, and `static modules` are replaced by one `Definition` with - `name`, `version`, `display`, `modules`, and `routes`. Events go in + `database.mongoDB.enable`, or `database.documentDB.enable`, or set + `DB_TYPE`. `DATABASE_URL` is the connection string only. +* **Integration classes use `static Definition`.** It replaces the 1.x + `static Config`, `static Options`, and `static modules`. Events go in `this.events`. -* **One `User` model.** The `IndividualUser` and `OrganizationUser` - discriminators are replaced by a `User` model with a `type` field. +* **One `User` model** with a `type` field replaces the `IndividualUser` and + `OrganizationUser` discriminators. * **New integration statuses.** Integrations start in `IN_CREATION` and pass - through `IN_DELETION` on teardown. Code that filters on status must handle - both. -* **Deployed stages always encrypt.** A function running in AWS Lambda - encrypts sensitive fields on every stage, `dev` included, and refuses to - start without a key (`EncryptionConfigurationError`). Set - `encryption: { fieldLevelEncryptionMethod: 'kms' }`, or provide `AES_KEY_ID` - and `AES_KEY`. Only local runs (`frigg start`, tests) skip encryption. To - store a deployed stage in plaintext on purpose, set - `fieldLevelEncryptionMethod: 'none'`. -* **Node.js 22, AWS SDK v3, and osls.** Lambda functions run on `nodejs22.x`. - Deploys use oss-serverless (`osls`) instead of the Serverless Framework. + through `IN_DELETION`. +* **Encryption fails closed in every deployed stage, `dev` included.** A + deployed function without a key refuses to start. Opt out only with + `fieldLevelEncryptionMethod: 'none'`. `BYPASS_ENCRYPTION_STAGE` is no longer + read (#663). +* **Module credential fields are encrypted on write** (#680). Existing rows + stay readable and are encrypted the next time they are written. +* **The v1 Management API is deprecated.** It still works and is frozen. v1 + responses carry `Deprecation` headers. v1 `POST /api/integrations` now + checks that the caller owns every entity (#671, #682). +* **New tables and columns.** Organization memberships and `organizationId` + on entities and integrations come with migrations. Run `frigg db:setup` or + apply them with `/admin/db-migrate` (#681, #682). +* **The app definition is validated.** Unknown keys and retired values stop + `frigg build` and `frigg deploy`. `database.postgres.management` takes + `discover`, `managed`, or `use-existing`, and `vpc.management` takes + `use-existing` instead of `existing`. Set `user.usePassword` and + `user.authModes` explicitly, because their schema defaults changed (#666). +* **Node.js 22.13+ or 24, with osls 4.** Deploys use oss-serverless 4 and + `infrastructure.js` instead of the Serverless Framework and a hand-written + `serverless.yml` (ADR-050). AWS SDK v3 replaces v2. ## Requirements -| Requirement | Frigg 2.0 | +| Requirement | Frigg 2.0.1 | | --- | --- | -| Node.js | 22 or later | +| Node.js | 22.13 or later, or 24 | | npm | 10 or later | | Database | PostgreSQL, MongoDB (replica set), or Amazon DocumentDB | -| Cloud | AWS (Lambda `nodejs22.x`, SQS, KMS, EventBridge Scheduler) | -| Deploy tool | `osls` (installed with `@friggframework/devtools`) | - -## 1. Run it on your database - -Frigg 2.0 stores data through Prisma behind repository interfaces. The same -integration code runs on PostgreSQL, MongoDB, and Amazon DocumentDB. - -```javascript -// backend/index.js -const Definition = { - name: 'my-app', - integrations: [HubSpotIntegration], - database: { postgres: { enable: true } }, // or mongoDB / documentDB - encryption: { fieldLevelEncryptionMethod: 'kms' }, // or 'aes' -}; -module.exports = { Definition }; -``` - -* `DATABASE_URL` holds the connection string. `frigg db:setup` checks it, - generates the Prisma client, and runs migrations (PostgreSQL) or `db push` - (MongoDB). -* Field-level encryption runs inside a Prisma client extension. Repositories - and your code see plain values. Credentials, mappings, password hashes, and - tokens are encrypted at rest with AWS KMS or AES. The `dev`, `test`, and - `local` stages skip encryption. -* PostgreSQL deployments get `/admin/db-migrate` endpoints to check and apply - schema migrations. `POST /admin/db-migrate/resolve` marks a failed migration - as applied or rolled back (#627). - -## 2. Operate it in production - -* **`frigg doctor`** checks a deployed CloudFormation stack. **`frigg repair`** - imports orphaned resources and reconciles drift. **`frigg generate-iam`** - writes a deployment IAM stack scoped to your app definition. `frigg deploy` - runs `doctor` after each deploy unless you pass `--skip-doctor`. -* **Stay under the 4 KB Lambda environment limit** (#625). Mark a variable - with `environment: { MY_SECRET: 'ssm' }` and Frigg stores it in SSM - Parameter Store and loads it at cold start. `frigg ssm push` uploads the - values, and `frigg deploy` runs it for you. `lambda.scopedEnvironment: true` - gives each function only the variables it uses. See - [ADR-027](../architecture-decisions/027-ssm-parameter-offload-and-env-scoping.md). -* **Structured JSON logs** (#654). Every record is one redacted JSON line with - `requestId`, `integrationId`, and OpenTelemetry `trace_id`/`span_id` when a - span is active. Use `this.logger` in integrations and API modules. Set - `logging: { level, retentionInDays }` in the app definition to control - `FRIGG_LOG_LEVEL` and CloudWatch retention. See [Logging](../guides/LOGGING.md). -* **OpenTelemetry and usage tracking** (#622). Deployed stages export nothing - until you set `telemetry.exporter` (`otlp`, `honeycomb`, `datadog`, or - `console`). Handler dispatch and API module requests are traced with no - code. Call `this.telemetry.span()` and `this.telemetry.count()` for your own - spans and counters. Declare `Definition.usage` on an integration and Frigg - keeps durable counters such as `records.synced` and `api.requests` in a - `UsageCounter` table. Reports and scripts read them through - `frigg.usage.getTotalsByDimension()` and `frigg.usage.getTimeSeries()`. See the - [telemetry guide](https://github.com/friggframework/frigg/blob/next/packages/core/telemetry/README.md). -* **Health endpoints**: `/health`, `/health/detailed` (includes an encryption - round trip), `/health/live`, and `/health/ready`. -* **Large dependency trees.** Set `lambda.keepNestedNodeModules: true` when a - package needs its own nested `node_modules` in the Lambda bundle (#645). - -## 3. Admin work inside your VPC - -**Admin scripts** (#517) run maintenance code in your deployed app, with the -same VPC, KMS, and database access as your integrations. Add -`adminScripts: [MyScript]` to the app definition and Frigg provisions the -router, executor Lambda, and queue. - -* A script extends `AdminScriptBase` and reads data through - `this.context.commands`. `this.context.instantiate(integrationId)` gives it a - live integration to call external APIs. -* `POST /admin/scripts/{name}` runs it in `sync` mode (inside the API call) or - `async` mode (queued, up to 15 minutes). Every run is stored as an - `AdminScriptExecution` record. -* `queueScript()` chains follow-up scripts. With - `admin: { enableScheduling: true }`, `PUT /admin/scripts/{name}/schedule` - creates an EventBridge schedule. - -**Reports** (#628) are admin operations whose output is the payload. A report -extends `ReportBase` and you register it with `reports: [MyReport]`. Set -`admin: { includeBuiltinReports: true }` to add the built-in `integrations` -report. - -* Run modes: `live` returns the result inline, `recorded` stores an execution - you poll, and `snapshot` stores a run in a named series for trends. -* `POST /api/v2/reports/{name}/run` starts a run. CSV, PDF, and ZIP output goes - to a private S3 bucket and comes back as a signed URL. -* Scripts, reports, and `/admin/db-migrate` all use the - `x-frigg-admin-api-key` header, checked against `ADMIN_API_KEY`. - -See [ADR-005](../architecture-decisions/005-admin-script-runner.md), -[ADR-010](../architecture-decisions/010-reporting-as-admin-operation.md), and -the [admin scripts README](https://github.com/friggframework/frigg/blob/next/packages/admin-scripts/README.md). - -## 4. Integrations that stay connected - -* **One refresh per expired token** (#636). When many requests on one module - get a 401 at once, they share a single refresh. A newer credential that - another Lambda already stored is adopted instead of refreshed again. -* **Grace retry on 401** (#623). Requesters retry a 401 up to three times, even - without a refresh token, before they invalidate the credential. -* **Refresh tokens are kept.** When a provider's refresh response omits - `refresh_token`, `OAuth2Requester` keeps the one it has. -* **Clear failure records.** When credentials fail, the integration records an - "Authentication Error" message with the module and status code, then moves - to `ERROR` (#634). A successful re-auth moves it back to `ENABLED`. - `FetchError` messages keep the sanitized cause (#659). -* **No lost writes.** `this.patchConfig(patch)` merges config atomically in the - database (#614). `updateProcessMetrics` writes counters atomically, so - concurrent workers no longer overwrite each other (#638). -* **Safe lifecycle.** A failing `ON_DELETE` handler no longer blocks uninstall - (#615). Re-authorizing reuses the existing integration instead of creating - a duplicate (#613). - -## 5. Build faster - -* **Integration extensions.** An API module can ship a webhook receiver, - signature check, and queue dispatch. You bind it with `Definition.extensions` - and write only the handler method. See the - [extensions guide](https://github.com/friggframework/frigg/blob/next/packages/core/integrations/EXTENSIONS.md). -* **Per-account webhooks.** Set `webhooks: true` on an integration Definition - and implement `onWebhook`. -* **One-time scheduled jobs.** `createSchedulerCommands()` schedules a future - event onto an integration's queue through EventBridge Scheduler. Use it for - webhook renewals and delayed work. -* **Final-attempt signal** (#653). Queue handlers receive - `delivery.isLastAttempt`, so you can close out a run before SQS moves the - message to the dead-letter queue. -* **`queryMappings`** (#651). Filter, sort, and page an integration's mappings - inside the database instead of loading every row. It requires the - `IntegrationMapping.mapping` field to be excluded from encryption. -* **`frigg auth`** tests an API module's OAuth2 or API-key flow locally and - saves the credentials: `frigg auth test`, `list`, `get`, and `delete`. - -## 6. Under the hood - -* **Hexagonal architecture.** Handlers call use cases. Use cases call - repositories. Repositories talk to the database and AWS. Each layer is - tested on its own, and each database has its own repository adapter. -* **oss-serverless.** `frigg build` and `frigg deploy` run - `osls --config infrastructure.js`. Your `infrastructure.js` calls - `createFriggInfrastructure()`, which builds the stack from your app - definition and discovers your VPC, subnets, and KMS keys. -* **AWS SDK v3** everywhere. See the - [SDK v3 and osls guide](../reference/aws-sdk-v3-osls-migration.md). -* **esbuild and a Prisma Lambda layer** keep function bundles small. The layer - ships only the Prisma runtime client. - -## 7. What's next - -These items are on the roadmap. They are not part of 2.0. - -* **Capabilities**: a typed statement of what an app, integration, or API - module can do, exposed to agents as MCP tools +| Host | AWS (Lambda `nodejs22.x`, SQS, KMS, EventBridge Scheduler) or Netlify | +| Deploy tool | `osls` 4 (installed with `@friggframework/devtools`) on AWS | +| Encryption | A KMS key or AES keys for every deployed stage | + +## What's next + +These items are not part of 2.0.1: + +* **Two-way sync in one file.** A `defineIntegration` sync with conflict + rules, built on the sync engine + ([ADR-054](https://github.com/friggframework/frigg/pull/669), + [ADR-044–047](https://github.com/friggframework/frigg/pull/648)). +* **Profile-aware scaffolding and default UIs.** ADR-056 proposes + `frigg init --profile product|operations` and a default UI for each + profile. +* **Capabilities as MCP tools**: a typed statement of what an app, + integration, or API module can do ([ADR-020](../architecture-decisions/020-capabilities.md)). -* **Agent harness and integration templates**: a structured runtime for agents - that build integrations, and reusable integration starting points +* **An agent harness and integration templates** ([ADR-025](../architecture-decisions/025-agent-harness.md), [ADR-023](../architecture-decisions/023-integration-templates.md)). -* **Fenestra** as a spec format that capabilities can point to. -* **Multi-step authorization** and shared entities. +* **Admin routes for `global` entities.** The tenant API refuses to create + them. See the [Roadmap](../roadmap/page-1.md) and the [ADR index](../architecture-decisions/README.md). @@ -224,11 +402,7 @@ See the [Roadmap](../roadmap/page-1.md) and the ## Start or upgrade * **New app:** install the CLI with - `npm install -g @friggframework/devtools@^2.0.1`. Then run + `npm install -g @friggframework/devtools@^2.0.1`, then run `frigg init my-app`, `frigg install `, and `frigg start`. -* **Existing 1.x app:** follow [Migrating from 1.x to 2.0](../guides/migrating-to-2.0.md). - -{% hint style="info" %} -If `frigg init` fails with "Backend template not found", upgrade -`@friggframework/devtools` to the latest 2.x release and run it again. -{% endhint %} +* **Existing 1.x app:** follow + [Migrating from 1.x to 2.0](../guides/migrating-to-2.0.md). diff --git a/docs/guides/migrating-to-2.0.md b/docs/guides/migrating-to-2.0.md index 39b76584e..79934225d 100644 --- a/docs/guides/migrating-to-2.0.md +++ b/docs/guides/migrating-to-2.0.md @@ -1,7 +1,8 @@ --- description: >- Upgrade a Frigg 1.x app to 2.0: Prisma instead of Mongoose, an explicit - database choice, Definition-based integrations, Node.js 22, and osls. + database choice, Definition-based integrations, fail-closed encryption, + Management API v2, Node.js 22.13+ or 24, and osls 4. --- # Migrating from 1.x to 2.0 @@ -28,14 +29,19 @@ so plan the data move as a migration, not an in-place upgrade. | Users | `IndividualUser` and `OrganizationUser` discriminators | One `User` model with a `type` field | | Integration class | `static Config`, `static Options`, `static modules` | One `static Definition` and `this.events` | | Infrastructure | Hand-written `serverless.yml` | `infrastructure.js` generated from the app definition | -| Deploy tool | Serverless Framework | `osls` (oss-serverless), run by `frigg deploy` | +| Deploy tool | Serverless Framework | `osls` 4 (oss-serverless), run by `frigg deploy` | | AWS SDK | v2 (`aws-sdk`) | v3 (`@aws-sdk/client-*`) | -| Runtime | Node.js 18 | Node.js 22 (`nodejs22.x` on Lambda) | +| Runtime | Node.js 18 | Node.js 22.13+ or 24 for the CLI; `nodejs22.x` on Lambda | +| Encryption | Skipped on `dev`, `test`, and `local` | Required on every deployed stage | +| Management API | Unversioned `/api/*` | `/api/v2/*`; v1 still served, frozen and deprecated | +| Entity ownership | The user who connected it | A scope: `individual`, `organization`, or `global` | ## Step 1: Upgrade Node.js -Install Node.js 22 or later and npm 10 or later, locally and in CI -(`nvm install 22`). +Install Node.js 22.13 or later (or Node.js 24) and npm 10 or later, locally +and in CI (`nvm install 22`). osls 4, which `frigg deploy` runs, does not +support Node.js 23 or 22.0 to 22.12. Lambda functions still run on +`nodejs22.x`. ## Step 2: Start from a 2.0 backend @@ -118,6 +124,10 @@ keep a deployed stage in plaintext on purpose, set `fieldLevelEncryptionMethod: 'none'`; Frigg logs a warning on every cold start. {% endhint %} +API modules that list `encryption.credentialFields` now have those fields +encrypted too. Before 2.0.1 they were stored in plaintext by mistake. Existing +rows stay readable and are encrypted the next time they are written. + ## Step 4: Create the schema ```bash @@ -125,7 +135,21 @@ frigg db:setup ``` This checks `DATABASE_URL`, generates the Prisma client, and runs migrations -(PostgreSQL) or `db push` (MongoDB and DocumentDB). +(PostgreSQL) or `db push` (MongoDB and DocumentDB). The schema includes the +2.0.1 tables for organization memberships, entity and integration +`organizationId` columns, and integration definitions. + +Then check the app definition: + +```bash +frigg validate --stage dev +``` + +`frigg build` and `frigg deploy` run the same check and stop on errors. +Unknown keys are errors. Rename retired values: `database.postgres.management` +takes `discover`, `managed`, or `use-existing`, and `vpc.management` takes +`use-existing` instead of `existing`. Set `user.usePassword` and +`user.authModes` explicitly, because their schema defaults changed. ## Step 5: Port your integration classes @@ -196,7 +220,44 @@ Core no longer exports `mongoose`, the Mongoose models (`UserModel`, * Handle the `IN_CREATION` and `IN_DELETION` integration statuses anywhere you filter or display status. -## Step 7: Move AWS SDK v2 calls to v3 +## Step 7: Point clients at Management API v2 + +Your 1.x clients keep working: 2.0.1 serves the v1 routes unchanged, frozen +and deprecated. Every v1 response carries `Deprecation` and `Link` headers, +and every v1 call is logged as `frigg.api.deprecated_route`, so you can find +the callers left to move. + +* Move each call with the v1 to v2 table in the + [API reference](../reference/api-reference.md). Clients can call + `GET /api/meta` first to see which versions the backend serves. +* In `@friggframework/ui`, pass `{ apiVersion: 'v2' }` to the `API` client + after the backend is on 2.0.1. +* One v1 change applies right away: `POST /api/integrations` now checks that + the caller owns every entity it names. +* When nothing calls v1, set `managementApi: { v1: false }`. + +**Entity scopes keep your current behavior.** With no scope configured, an +entity is `individual` (owned by the person who connected it), or +`organization` when `user.primary` is `'organization'`. That matches 1.x. No +data backfill is needed. To share connections across an organization, see the +entity scopes guide. + +**Optional: adopter tokens.** If your product already issues JWTs, Frigg can +accept them instead of `x-frigg-*` headers or Frigg logins. This is the +browser-safe option for a multi-tenant product: + +```javascript +user: { + authModes: { adopterJwt: true }, + jwtConfig: { + jwksUri: 'https://auth.example.com/.well-known/jwks.json', + issuer: 'https://auth.example.com/', + audience: 'frigg', + }, +}, +``` + +## Step 8: Move AWS SDK v2 calls to v3 Only custom code that calls AWS directly needs this. @@ -208,7 +269,7 @@ await new SQSClient({}).send(new SendMessageCommand(params)); See [AWS SDK v3 and oss-serverless](../reference/aws-sdk-v3-osls-migration.md). -## Step 8: Move your data +## Step 9: Move your data There is no automated 1.x data migration. The tool is designed in [ADR-004](../architecture-decisions/004-migration-tool-design.md) but has not @@ -222,7 +283,7 @@ shipped. Pick one path: `packages/core/prisma-postgresql/`. Check on staging that migrated credentials decrypt and refresh before you cut over. -## Step 9: Deploy and verify +## Step 10: Deploy and verify ```bash frigg build --production @@ -267,13 +328,14 @@ skip them. ## Checklist -* [ ] Node.js 22+ and npm 10+ locally and in CI +* [ ] Node.js 22.13+ or 24, and npm 10+, locally and in CI * [ ] `@friggframework/core` and `@friggframework/devtools` on `^2.0.1`, plus `prisma` and `@prisma/client` * [ ] API modules on versions that depend on core 2.x * [ ] `infrastructure.js` in place; `serverless.yml` removed * [ ] `database..enable` (or `DB_TYPE`) set, and `DATABASE_URL` replaces `MONGO_URI` * [ ] Encryption configured for every deployed stage (`kms`, AES keys, or an explicit `none`) -* [ ] `frigg db:setup` run +* [ ] `frigg db:setup` run; `frigg validate` passes +* [ ] Clients moved to `/api/v2` (or a plan to move them); v1 still works meanwhile * [ ] Integrations use `static Definition`, `this.events`, and `this..api` * [ ] No imports of removed core exports; `user.type` checks in place * [ ] Custom AWS calls on SDK v3 From d5e77cf8e8e601a607bbe83a03e4d565a7837fcd Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 25 Oct 2026 01:22:30 +0000 Subject: [PATCH 8/8] docs(2.0): limit release notes to work merged on next; keep people/agents vision Rewrite whats-new and trim the migration guide so they only claim work on next. Unmerged items move to In review with ADR/PR pointers. Keep the agents/capabilities vision line as approved. Co-authored-by: Sean Matthews --- docs/getting-started/whats-new-in-2.0.md | 423 +++-------------------- docs/guides/migrating-to-2.0.md | 65 +--- 2 files changed, 64 insertions(+), 424 deletions(-) diff --git a/docs/getting-started/whats-new-in-2.0.md b/docs/getting-started/whats-new-in-2.0.md index 0744f6a7c..7a74732e4 100644 --- a/docs/getting-started/whats-new-in-2.0.md +++ b/docs/getting-started/whats-new-in-2.0.md @@ -1,408 +1,93 @@ --- description: >- - Frigg 2.0 is the integration backend for people and their agents: three ways - to define an integration on one runtime, connections that agents use without - seeing a credential, two app profiles, production defaults, and more than one - host. + Frigg 2.0 is the integration backend for people and their agents: a Prisma + data layer, production defaults, a CLI that scaffolds, validates, and deploys, + and a runtime that keeps connections up. --- # What's New in Frigg 2.0 -**Frigg 2.0 is the integration backend for people and their agents.** Each -person and each organization connects their own accounts once. People, your -product, and the agents working for them then use those connections through -one versioned API, and nobody outside Frigg ever holds the credential. +**Frigg 2.0 is the integration backend for people and their agents.** You get one data layer for PostgreSQL, MongoDB, and DocumentDB; a CLI that scaffolds, validates, and deploys; and a runtime that keeps connections healthy under load. Typed capabilities and agent-ready connections are in review for a later 2.0.x — see [In review](#in-review-not-in-201-yet). -Frigg 2.0.1 is the first stable 2.x release. Install `^2.0.1`. Do not use -`^2.0.0`, which can resolve to a deprecated 2024 build of -`@friggframework/core`. +Frigg 2.0.1 is the first stable 2.x release. Install `^2.0.1`. Do not use `^2.0.0`, which can resolve to a deprecated 2024 build of `@friggframework/core`. {% hint style="warning" %} -2.0 has breaking changes. If you run a 1.x app, read -[Migrating from 1.x to 2.0](../guides/migrating-to-2.0.md) before you upgrade. +2.0 has breaking changes. If you run a 1.x app, read [Migrating from 1.x to 2.0](../guides/migrating-to-2.0.md) before you upgrade. {% endhint %} -2.0 is built on five ideas: +## Define integrations in code -1. [Three ways to define an integration, one runtime](#1-three-ways-to-define-an-integration-one-runtime) -2. [Connections agents can use without seeing a credential](#2-connections-agents-can-use-without-seeing-a-credential) -3. [Two app profiles: product and operations](#3-two-app-profiles-product-and-operations) -4. [Production by default](#4-production-by-default) -5. [More than one host](#5-more-than-one-host) +A class that extends `IntegrationBase` with a `static Definition` declares its modules, routes, and events. Events go in `this.events`. -{% hint style="info" %} -**We run it ourselves.** Left Hook, the company that maintains Frigg, runs -both profiles on 2.0. One is an internal agent tool server with hundreds of -tools, where every person's connections live in Frigg instead of in shared -environment variables (the operations profile, on Netlify). The other is a -product where customers' integrations are built from a catalogue or by chat -and validated by Frigg (the product profile, on AWS). 2.0.1 ships when both -run on a published `next` prerelease. -{% endhint %} - -## 1. Three ways to define an integration, one runtime - -An integration can be code, a validated JSON document, or an entry in your -app's catalogue. All three install, list, route, run, and log the same way. - -### In code - -A class that extends `IntegrationBase` with a `static Definition` works as it -did in every 2.0 prerelease. 2.0.1 adds `defineIntegration`, which builds that -class from one object -([ADR-054](https://github.com/friggframework/frigg/pull/669)): - -```javascript -const { defineApp, defineIntegration, use } = require('@friggframework/core'); - -const DealAlerts = defineIntegration({ - name: 'deal-alerts', - version: '1.0.0', - display: { label: 'Deal alerts in Slack', category: 'Notifications' }, - modules: { crm: use(hubspot), chat: use(slack) }, - on: { - 'crm.deal.changed': async ({ deal }, { modules: { chat }, config }) => { - /* post to config.channel */ - }, - }, - triggers: { digest: { every: '1d', handler: sendDigest } }, // ADR-055 -}); - -module.exports = defineApp({ name: 'my-app', integrations: [DealAlerts] }); -``` - -`defineIntegration` returns an `IntegrationBase` subclass, so everything that -reads `Definition` today works with it. - -### As a document - -An integration can also be a JSON document -([ADR-063](https://github.com/friggframework/frigg/pull/692)). It declares -modules, settings, syncs, and workflows. It has no HTTP or script step: -every step is either a built-in step or a capability -(`..`) that the module's capability manifest declares. - -```json -{ - "schemaVersion": "0.1", - "id": "pipedrive-hubspot", - "version": "1.0.0", - "display": { "label": "Pipedrive ⇄ HubSpot people", "category": "CRM" }, - "modules": { - "pipedrive": { "module": "pipedrive", "version": "^2.1" }, - "hubspot": { "module": "hubspot", "version": "^3" } - }, - "syncs": [{ - "id": "people", - "source": "pipedrive.person", - "target": "hubspot.contact", - "direction": "two_way", - "identity": { "match": [{ "source": "emails[0].value", "target": "properties.email", "transform": "lowercase", "key": "email" }] }, - "mapping": [{ "source": "first_name", "target": "properties.firstname", "transform": "trim" }], - "backfill": { "trigger": "userAction", "pageSize": 100 }, - "ongoing": { "mode": "webhook" }, - "default": "on" - }] -} -``` - -This is abridged from `examples/pipedrive-hubspot.definition.json`. That -example also subscribes the webhook in `lifecycle.onInstall`. - -* **Validated twice.** Frigg checks a document against the JSON Schema and - then checks its references. Each capability must exist in a manifest, and - each manifest method must exist on the real API module. Run - `npm run validate:definitions --workspace @friggframework/core` in CI. -* **Published as immutable versions.** `POST /admin/integrations/versions` - publishes a version. A published version never changes. Frigg refuses a - different document at the same version, and a version whose semver bump is - smaller than its changes. Each installation pins a version and a checksum. - `GET`/`POST /api/integrations/:integrationId/upgrade` plans and applies an - upgrade. -* **Installed like code.** A published id is an integration type. Install it - with `POST /api/integrations` and `config: { type: '' }`. It lists, - routes, and logs under its own name. When code and a document share a name, - code wins. -* **Code can embed a document.** - `extendIntegrationClass(MyIntegration, { definition, manifests })` adds a - document's syncs and workflows to a code class. A `code` step with - `ref: "#methodName"` calls a method on that class. - -### From a catalogue - -`GET /api/integration-definitions` lists the latest published version of each -definition your app offers. Workspaces can publish their own definitions with -`POST /api/integration-definitions`. Other workspaces never see them. - -### What a definition can do - -* **Workflows** run on a webhook, a schedule, a user action, or a synced - record. Steps include `map`, `branch`, `foreach`, `links.find`, and - `links.upsert`. `delay` (`for: '2h'`) pauses a run, and `approval` waits for - a person. A person approves or rejects with - `POST /api/integrations/:integrationId/runs/:runId/decision`. Every run is - stored and listed at `GET /api/integrations/:integrationId/runs`. -* **A sync engine.** A sync has a source, a target, a direction (one-way or - `two_way`), identity matching, a field mapping, a backfill, and ongoing - updates by webhook or by polling. -* **Recurring triggers** ([ADR-055](https://github.com/friggframework/frigg/pull/669)). - `Definition.triggers` takes `every: '15m'` or a five-field `cron` with a - `timezone`. With `configurable: true`, each installation picks its own - interval. One shared tick runs every trigger, so Frigg does not create a - schedule per installation. -* **Previews and dry runs.** `previewWorkflow()` runs a workflow against - recorded calls and returns what it would send, without calling any API. - `previewSurface()` renders an integration's UI as Slack blocks, a HubSpot - card, or HTML. - -## 2. Connections agents can use without seeing a credential - -### Management API v2 - -([ADR-053](https://github.com/friggframework/frigg/pull/668), #671) - -* Every route lives under `/api/v2/*`. Errors share one shape, and every - response carries `Frigg-API-Version: 2`. A resource that does not exist and - one owned by someone else return the same `404`. -* `GET /api/meta` needs no authentication. It returns the API versions, their - status, their OpenAPI documents, and feature flags such as `entityProxy`. -* Entities never return credential fields. `GET /api/v2/credentials` returns - credentials with their secrets masked. -* `GET`/`POST /api/v2/authorize` handles single-step and multi-step - authorization. -* **v1 still works, but it is frozen and deprecated.** Every v1 response - carries `Deprecation` and `Link` headers, and each v1 call is logged and - counted. v1 will not be removed before core 3.0. When nothing uses v1, set - `managementApi: { v1: false }`. See the - [API reference](../reference/api-reference.md) for the v1 to v2 table. - -### Who is calling - -* **Adopter tokens** (#678). Your product or your agent host signs a JWT and - sends it as `Authorization: Bearer `. Turn it on with - `user.authModes.adopterJwt: true` and `user.jwtConfig` (`jwksUri` or - `secret`, `issuer`, `audience`). Frigg finds or creates the user and the - organization named in the token's claims (`sub` and `org_id` by default). -* **Organization memberships** (#681). A person can belong to several - organizations. Each request acts in one active organization: the token's - `org_id` claim or the `x-frigg-apporgid` header. -* **Organization roles** (#683). Members can use the organization's - connections. Creating, changing, or deleting them needs a role listed in - `user.orgAdminRoles` (default `['ADMIN']`). -* **Bearer tokens resolve to their owner** (#691, merged). A Frigg bearer - token now acts as the user who owns it. - -### Whose connection it is - -([ADR-056](https://github.com/friggframework/frigg/pull/670), #682) - -* Every entity has a scope: `individual` (one person), `organization` (shared - by the organization), or `global` (shared by the whole app). -* The default follows `user.primary`: `organization` when it is - `'organization'`, otherwise `individual`. Existing apps keep their behavior. - You can set a scope per module binding, per integration - (`Definition.ownership`), per entity type, or for the whole app - (`appDefinition.ownership`), and you can let the caller choose from an - `allow` list. -* Lists filter by `?scope=` and `?owner=me|org`, and each item says what the - caller may do with it (`access`). -* Credentials are scoped through their entities (#684). Frigg refuses to - delete a credential that another organization still uses. -* **App-level entity registry** (ADR-059, #679). - `appDefinition.entities: { : { definition, scope, proxy } }` - lets people connect a module that no integration uses yet. For example, a - person can connect an account only so an agent can use it. -* **Attach and detach** (#689, merged). - `addEntitiesToIntegration` and `removeEntitiesFromIntegration` change the - connections on an existing integration. - -### The entity proxy - -([ADR-052](https://github.com/friggframework/frigg/pull/668), #671, #685, #690) - -`POST /api/v2/entities/:entityId/proxy` calls the connected account's API -with the stored credential. The caller sends a method and a path. Frigg adds -the credential, refreshes the token if needed, and returns the response. The -caller never sees the token, which is what lets you hand a connection to an -agent. - -```text -POST /api/v2/entities/7/proxy -{ "method": "GET", "path": "/crm/v3/objects/contacts", "query": { "limit": 10 } } -``` - -* **Off by default and denied by default.** Turn it on with - `managementApi.proxy.enable: true`. Each API module lists the methods and - paths it allows in `proxy.allow`, and the app can narrow that list per - module. -* **The host always comes from the module.** Frigg refuses absolute URLs, - `..`, encoded separators, and credential headers. A module that spans - several hosts declares named `proxy.baseUrls`, and the request picks one by - name (`"base": "docs"`), never by URL (#690). -* **Scopes.** `managementApi.proxy.scopes` (default `['individual']`) decides - whose connections the proxy may use. Add `'organization'` to allow shared - ones. `'global'` is never allowed (#685). -* Every call is audited as `frigg.api.proxy.request`. Upstream 429s keep - `Retry-After`. - -## 3. Two app profiles: product and operations - -([ADR-056](https://github.com/friggframework/frigg/pull/670)) +* **Integration extensions** (`Definition.extensions`) bind an API module's ready-made routes, events, and queue dispatch. +* **Per-account webhooks** with `Definition.webhooks: true`. +* **One-time jobs** with `createSchedulerCommands()`. +* **Mappings** you can claim atomically (`createMapping`, #674) and query by content (`queryMappings`, #651). +* **Entities added to or removed from a live integration** (#689). +* **`delivery.isLastAttempt`** on queue handlers, so a run can fail cleanly on its last try (#653). -| | Product | Operations | -| --- | --- | --- | -| Who adopts | A software company building integrations for its customers | A company, a household, or one person automating its own work | -| Tenancy | Many customer organizations in one shared deployment, each isolated | One organization | -| Who connects accounts | Each customer and each of their users | Each member connects their own, and the organization shares some | -| Typical auth | Adopter JWT from the product | Staff login, or adopter JWT from an internal tool | +## Production by default -Both profiles use the same runtime and the same Management API. The difference -is configuration: the user model, the default entity scope, and which scopes -the proxy allows. +* **A scaffold that works and costs almost nothing idle** (#639). `frigg init` writes a backend that installs, starts, and deploys. It has no VPC and no NAT gateway, uses your own PostgreSQL through `DATABASE_URL`, and encrypts with KMS. +* **`frigg validate`** (#666). It checks your app definition against a schema that matches what the runtime reads, and reports each error with a JSON pointer and a hint. `frigg build` and `frigg deploy` run it first. +* **SSM offload** for large environments, with per-function env scoping (#625). -## 4. Production by default +## Operations -* **Encryption fails closed** (#663). Every deployed stage encrypts sensitive - fields, `dev` included. A deployed function with no key refuses to start - with `EncryptionConfigurationError`. Set - `encryption: { fieldLevelEncryptionMethod: 'kms' }`, or provide `AES_KEY_ID` - and `AES_KEY`. Only local runs (`frigg start`, tests) skip encryption. To - store a deployed stage in plaintext on purpose, set - `fieldLevelEncryptionMethod: 'none'`. Frigg then logs a warning on every - cold start. -* **Module credentials are encrypted too** (#680). Fields an API module lists - in `encryption.credentialFields` are now encrypted. Before 2.0.1 they were - silently stored in plaintext. Reads decrypt only values that have the exact - ciphertext shape, so stored URLs that contain colons are no longer mistaken - for ciphertext. -* **A scaffold that works and costs almost nothing idle** (#639). `frigg init` - writes a backend that installs, starts, and deploys. It has no VPC and no - NAT gateway, uses your own PostgreSQL through `DATABASE_URL`, and encrypts - with KMS. Idle, it costs about $1 a month for the KMS key. -* **`frigg validate`** (#666, - [ADR-051](https://github.com/friggframework/frigg/pull/666)). It checks your - app definition against a schema that now matches what the runtime reads. It - reports each error with a JSON pointer and a hint. `frigg build` and - `frigg deploy` run it first and stop on errors. -* **osls 4** ([ADR-050](https://github.com/friggframework/frigg/pull/664)). - Deploys move to oss-serverless 4. The CLI needs Node.js 22.13 or later, or - Node.js 24. Lambda functions stay on `nodejs22.x`. -* **Rate limits that wait instead of fail** (ADR-049, #656–#658, if merged). - An API module declares `static rateLimit`. The requester honours - `Retry-After`. A queue message that hits a long limit goes back on the queue - at its retry time instead of failing. When the wait is too long, the - integration records a warning the user can see. +* **Structured JSON logs** (#654), with `trace_id` and `span_id` inside an OTel span. +* **Opt-in OpenTelemetry traces and metrics** and usage counters (#622). +* **Health endpoints** at `/health`, `/health/detailed`, `/health/live`, and `/health/ready`. +* **`frigg doctor`, `frigg repair`, and `frigg generate-iam`.** +* **Admin scripts** (#517) and **reports** with `live`, `recorded`, and `snapshot` run modes (#628), behind `x-frigg-admin-api-key`. Admin queues are created locally on `frigg start` (#675). -## 5. More than one host +## Connections that stay up -* **AWS by default.** Lambda, SQS, KMS, and EventBridge Scheduler, generated - from your app definition. -* **Netlify as a second provider** - ([ADR-028](https://github.com/friggframework/frigg/pull/644), #686). Set - `provider: 'netlify'` in the app definition. `frigg build`, `frigg deploy`, - and `frigg start` then use Netlify Functions, background functions for - queues, and a scheduled function for recurring work. `frigg doctor`, - `repair`, and `generate-iam` stay AWS-only. +* Concurrent 401s on one API module share one token refresh (#636). +* A 401 gets a grace retry (#623). The existing `refresh_token` is kept when a provider omits it. +* Integrations record why a credential failed (#634). +* Config and process metrics update atomically (#614, #638). ## Under the hood -* **Your database.** A Prisma data layer behind repositories runs the same - code on PostgreSQL, MongoDB, and Amazon DocumentDB. Choose the engine with - `database..enable` or `DB_TYPE`. `frigg db:setup` generates the - client and applies the schema. -* **Operations tooling.** `frigg doctor`, `frigg repair`, and - `frigg generate-iam`. SSM offload for large environments (#625). Structured - JSON logs (#654). Opt-in OpenTelemetry and usage counters (#622). Health - endpoints at `/health`, `/health/detailed`, `/health/live`, and - `/health/ready`. -* **Admin work inside your VPC.** Admin scripts (#517) and reports with - `live`, `recorded`, and `snapshot` run modes (#628), both behind - `x-frigg-admin-api-key`. -* **Connections that stay up.** Concurrent 401s share one token refresh - (#636). A 401 gets a grace retry (#623). The existing `refresh_token` is kept - when a provider omits it. Integrations record why a credential failed (#634). - Config and process metrics update atomically (#614, #638). -* **Less plumbing.** Integration extensions (`Definition.extensions`), - per-account webhooks, one-time jobs with `createSchedulerCommands()`, - `delivery.isLastAttempt` (#653), `queryMappings` (#651), and `frigg auth` - for testing a module's auth locally. -* **Hexagonal architecture.** Handlers call use cases, and use cases call - repositories. AWS SDK v3, esbuild bundles, and a Prisma Lambda layer. +* **Your database.** A Prisma data layer behind repositories runs the same code on PostgreSQL, MongoDB, and Amazon DocumentDB. Choose the engine with `database..enable` or `DB_TYPE`. `frigg db:setup` generates the client and applies the schema. +* **Hexagonal architecture.** Handlers call use cases, and use cases call repositories. AWS SDK v3, esbuild bundles, and a Prisma Lambda layer. +* **Hosts.** AWS (Lambda `nodejs22.x`, SQS, KMS, EventBridge Scheduler) deployed, and local with `frigg start`. ## Breaking changes at a glance -* **Mongoose is gone.** Data access runs on Prisma. Core no longer exports - `mongoose`, the Mongoose models, `connectToDatabase`, `createFriggBackend`, - `ModuleManager`, `Auther`, or `IntegrationFactory`. -* **You pick the database explicitly.** Set `database.postgres.enable`, - `database.mongoDB.enable`, or `database.documentDB.enable`, or set - `DB_TYPE`. `DATABASE_URL` is the connection string only. -* **Integration classes use `static Definition`.** It replaces the 1.x - `static Config`, `static Options`, and `static modules`. Events go in - `this.events`. -* **One `User` model** with a `type` field replaces the `IndividualUser` and - `OrganizationUser` discriminators. -* **New integration statuses.** Integrations start in `IN_CREATION` and pass - through `IN_DELETION`. -* **Encryption fails closed in every deployed stage, `dev` included.** A - deployed function without a key refuses to start. Opt out only with - `fieldLevelEncryptionMethod: 'none'`. `BYPASS_ENCRYPTION_STAGE` is no longer - read (#663). -* **Module credential fields are encrypted on write** (#680). Existing rows - stay readable and are encrypted the next time they are written. -* **The v1 Management API is deprecated.** It still works and is frozen. v1 - responses carry `Deprecation` headers. v1 `POST /api/integrations` now - checks that the caller owns every entity (#671, #682). -* **New tables and columns.** Organization memberships and `organizationId` - on entities and integrations come with migrations. Run `frigg db:setup` or - apply them with `/admin/db-migrate` (#681, #682). -* **The app definition is validated.** Unknown keys and retired values stop - `frigg build` and `frigg deploy`. `database.postgres.management` takes - `discover`, `managed`, or `use-existing`, and `vpc.management` takes - `use-existing` instead of `existing`. Set `user.usePassword` and - `user.authModes` explicitly, because their schema defaults changed (#666). -* **Node.js 22.13+ or 24, with osls 4.** Deploys use oss-serverless 4 and - `infrastructure.js` instead of the Serverless Framework and a hand-written - `serverless.yml` (ADR-050). AWS SDK v3 replaces v2. +* **Mongoose is gone.** Data access runs on Prisma. Core no longer exports `mongoose`, the Mongoose models, `connectToDatabase`, `createFriggBackend`, `ModuleManager`, `Auther`, or `IntegrationFactory`. +* **You pick the database explicitly.** Set `database.postgres.enable`, `database.mongoDB.enable`, or `database.documentDB.enable`, or set `DB_TYPE`. `DATABASE_URL` is the connection string only. +* **Integration classes use `static Definition`.** It replaces the 1.x `static Config`, `static Options`, and `static modules`. +* **One `User` model** with a `type` field replaces the `IndividualUser` and `OrganizationUser` discriminators. +* **New integration statuses.** Integrations start in `IN_CREATION` and pass through `IN_DELETION`. +* **The app definition is validated.** Unknown keys and retired values stop `frigg build` and `frigg deploy`. `database.postgres.management` takes `discover`, `managed`, or `use-existing`, and `vpc.management` takes `use-existing` instead of `existing`. Set `user.usePassword` and `user.authModes` explicitly, because their schema defaults changed (#666). +* **Node.js 22 or later.** AWS SDK v3 replaces v2. ## Requirements | Requirement | Frigg 2.0.1 | | --- | --- | -| Node.js | 22.13 or later, or 24 | +| Node.js | 22 or later | | npm | 10 or later | | Database | PostgreSQL, MongoDB (replica set), or Amazon DocumentDB | -| Host | AWS (Lambda `nodejs22.x`, SQS, KMS, EventBridge Scheduler) or Netlify | -| Deploy tool | `osls` 4 (installed with `@friggframework/devtools`) on AWS | -| Encryption | A KMS key or AES keys for every deployed stage | +| Host | AWS (Lambda `nodejs22.x`, SQS, KMS, EventBridge Scheduler); local with `frigg start` | +| Deploy tool | `osls` (installed with `@friggframework/devtools`) | -## What's next +## In review, not in 2.0.1 yet -These items are not part of 2.0.1: +These items have open pull requests or proposed ADRs. They are not on `next` and may change: -* **Two-way sync in one file.** A `defineIntegration` sync with conflict - rules, built on the sync engine - ([ADR-054](https://github.com/friggframework/frigg/pull/669), - [ADR-044–047](https://github.com/friggframework/frigg/pull/648)). -* **Profile-aware scaffolding and default UIs.** ADR-056 proposes - `frigg init --profile product|operations` and a default UI for each - profile. -* **Capabilities as MCP tools**: a typed statement of what an app, - integration, or API module can do - ([ADR-020](../architecture-decisions/020-capabilities.md)). -* **An agent harness and integration templates** - ([ADR-025](../architecture-decisions/025-agent-harness.md), - [ADR-023](../architecture-decisions/023-integration-templates.md)). -* **Admin routes for `global` entities.** The tenant API refuses to create - them. +* `defineIntegration` and one-file integrations (ADR-054, #703); recurring triggers (ADR-055, #694); integration definitions as data (ADR-063). +* Management API v2, the entity proxy, entity scopes, and app profiles (ADR-052, 053, 056, 059; #671, #679, #681, #682, #684, #685, #690). +* Encryption that fails closed in deployed stages (#663) and encrypted module credential fields (#680). +* osls 4 (ADR-050, #704). +* Rate limits that wait instead of fail (ADR-049, #656–#658). +* Netlify as a second host (#686). +* Capabilities, an agent harness, and integration templates (ADR-020, 025, 023). -See the [Roadmap](../roadmap/page-1.md) and the -[ADR index](../architecture-decisions/README.md). +See the [Roadmap](../roadmap/page-1.md) and the [ADR index](../architecture-decisions/README.md). ## Start or upgrade -* **New app:** install the CLI with - `npm install -g @friggframework/devtools@^2.0.1`, then run - `frigg init my-app`, `frigg install `, and `frigg start`. -* **Existing 1.x app:** follow - [Migrating from 1.x to 2.0](../guides/migrating-to-2.0.md). +* **New app:** install the CLI with `npm install -g @friggframework/devtools@^2.0.1`, then run `frigg init my-app`, `frigg install `, and `frigg start`. +* **Existing 1.x app:** follow [Migrating from 1.x to 2.0](../guides/migrating-to-2.0.md). diff --git a/docs/guides/migrating-to-2.0.md b/docs/guides/migrating-to-2.0.md index 79934225d..8bae71e53 100644 --- a/docs/guides/migrating-to-2.0.md +++ b/docs/guides/migrating-to-2.0.md @@ -1,8 +1,8 @@ --- description: >- Upgrade a Frigg 1.x app to 2.0: Prisma instead of Mongoose, an explicit - database choice, Definition-based integrations, fail-closed encryption, - Management API v2, Node.js 22.13+ or 24, and osls 4. + database choice, Definition-based integrations, a validated app definition, + and Node.js 22 or later. --- # Migrating from 1.x to 2.0 @@ -29,19 +29,14 @@ so plan the data move as a migration, not an in-place upgrade. | Users | `IndividualUser` and `OrganizationUser` discriminators | One `User` model with a `type` field | | Integration class | `static Config`, `static Options`, `static modules` | One `static Definition` and `this.events` | | Infrastructure | Hand-written `serverless.yml` | `infrastructure.js` generated from the app definition | -| Deploy tool | Serverless Framework | `osls` 4 (oss-serverless), run by `frigg deploy` | +| Deploy tool | Serverless Framework | `osls` (oss-serverless), run by `frigg deploy` | | AWS SDK | v2 (`aws-sdk`) | v3 (`@aws-sdk/client-*`) | -| Runtime | Node.js 18 | Node.js 22.13+ or 24 for the CLI; `nodejs22.x` on Lambda | -| Encryption | Skipped on `dev`, `test`, and `local` | Required on every deployed stage | -| Management API | Unversioned `/api/*` | `/api/v2/*`; v1 still served, frozen and deprecated | -| Entity ownership | The user who connected it | A scope: `individual`, `organization`, or `global` | +| Runtime | Node.js 18 | Node.js 22 or later; `nodejs22.x` on Lambda | ## Step 1: Upgrade Node.js -Install Node.js 22.13 or later (or Node.js 24) and npm 10 or later, locally -and in CI (`nvm install 22`). osls 4, which `frigg deploy` runs, does not -support Node.js 23 or 22.0 to 22.12. Lambda functions still run on -`nodejs22.x`. +Install Node.js 22 or later and npm 10 or later, locally and in CI +(`nvm install 22`). Lambda functions run on `nodejs22.x`. ## Step 2: Start from a 2.0 backend @@ -111,23 +106,6 @@ DATABASE_URL="mongodb://localhost:27017/frigg?replicaSet=rs0" Keep your encryption keys. 2.0 reads the same variables as 1.x: `KMS_KEY_ARN` for KMS, or `AES_KEY_ID` and `AES_KEY` for AES. -{% hint style="warning" %} -**2.0 fails closed.** In 1.x the `dev`, `test`, and `local` stages skipped -encryption, and a stage without a key wrote plaintext. In 2.0 every deployed -stage, `dev` included, encrypts, and a deployed function with no key refuses to -start with `EncryptionConfigurationError`. Set -`encryption: { fieldLevelEncryptionMethod: 'kms' }` and Frigg creates or -discovers the key on every stage. Rows already stored in plaintext stay -readable and are encrypted the next time they are written. Only local runs -(`frigg start`, tests) still skip encryption on `dev`, `test`, and `local`. To -keep a deployed stage in plaintext on purpose, set -`fieldLevelEncryptionMethod: 'none'`; Frigg logs a warning on every cold start. -{% endhint %} - -API modules that list `encryption.credentialFields` now have those fields -encrypted too. Before 2.0.1 they were stored in plaintext by mistake. Existing -rows stay readable and are encrypted the next time they are written. - ## Step 4: Create the schema ```bash @@ -220,27 +198,9 @@ Core no longer exports `mongoose`, the Mongoose models (`UserModel`, * Handle the `IN_CREATION` and `IN_DELETION` integration statuses anywhere you filter or display status. -## Step 7: Point clients at Management API v2 - -Your 1.x clients keep working: 2.0.1 serves the v1 routes unchanged, frozen -and deprecated. Every v1 response carries `Deprecation` and `Link` headers, -and every v1 call is logged as `frigg.api.deprecated_route`, so you can find -the callers left to move. - -* Move each call with the v1 to v2 table in the - [API reference](../reference/api-reference.md). Clients can call - `GET /api/meta` first to see which versions the backend serves. -* In `@friggframework/ui`, pass `{ apiVersion: 'v2' }` to the `API` client - after the backend is on 2.0.1. -* One v1 change applies right away: `POST /api/integrations` now checks that - the caller owns every entity it names. -* When nothing calls v1, set `managementApi: { v1: false }`. +## Step 7: Keep your API clients -**Entity scopes keep your current behavior.** With no scope configured, an -entity is `individual` (owned by the person who connected it), or -`organization` when `user.primary` is `'organization'`. That matches 1.x. No -data backfill is needed. To share connections across an organization, see the -entity scopes guide. +The Management API routes stay unversioned under `/api/*` in 2.0.1, so existing clients keep working. **Optional: adopter tokens.** If your product already issues JWTs, Frigg can accept them instead of `x-frigg-*` headers or Frigg logins. This is the @@ -318,24 +278,19 @@ skip them. deprecated and is only deployed when you register `reports` or set `admin: { includeBuiltinReports: true }`. Call `POST /api/v2/reports/integrations/run` with `{ "mode": "live" }` instead. -* **Deployed `dev` stages now encrypt.** Prereleases skipped encryption on - `dev`, `test`, and `local` even when deployed. A deployed stage without a key - now fails at startup; add `encryption: { fieldLevelEncryptionMethod: 'kms' }` - and redeploy. `BYPASS_ENCRYPTION_STAGE` is no longer read. * **Process updates are atomic.** `updateProcessMetrics` and the context updates in `updateProcessState` no longer read, modify, and write the whole record. No code change is needed. ## Checklist -* [ ] Node.js 22.13+ or 24, and npm 10+, locally and in CI +* [ ] Node.js 22+, and npm 10+, locally and in CI * [ ] `@friggframework/core` and `@friggframework/devtools` on `^2.0.1`, plus `prisma` and `@prisma/client` * [ ] API modules on versions that depend on core 2.x * [ ] `infrastructure.js` in place; `serverless.yml` removed * [ ] `database..enable` (or `DB_TYPE`) set, and `DATABASE_URL` replaces `MONGO_URI` -* [ ] Encryption configured for every deployed stage (`kms`, AES keys, or an explicit `none`) +* [ ] Encryption keys carried over (`KMS_KEY_ARN`, or `AES_KEY_ID` and `AES_KEY`) * [ ] `frigg db:setup` run; `frigg validate` passes -* [ ] Clients moved to `/api/v2` (or a plan to move them); v1 still works meanwhile * [ ] Integrations use `static Definition`, `this.events`, and `this..api` * [ ] No imports of removed core exports; `user.type` checks in place * [ ] Custom AWS calls on SDK v3