Upgrading
On this page
Some releases need an operator, not just a deploy. This page lists those steps release by release, latest first. Each section is ordered: do the steps in the order given, because some of them cannot be repeated once the new engine has started. Take a database backup before any upgrade.
For what changed in each release, see the Changelog.
Upgrading to 1.2.0
Note. The managed-services build ships this release as
1.2.0-ee, the open-source build as1.2.0. Steps marked (managed services) apply to the managed-services build only; the rest apply to both. Where a migration has a different number in each build, the step gives both.
This release moves the engine to Node 24, moves several former environment variables into the product, moves four tool families into plugins, changes how Telegram and Slack receive messages, makes Web a channel that new agents start without, and with managed services moves every provider key into a connection bound to a role. Most steps are preparation you can do before the upgrade window. The ones marked as warnings lose data or access if they are skipped or done in the wrong order.
Before the upgrade window
1. Back up and record what you have. Take a database backup and keep it until the new release has run cleanly for a while: there is no down migration, so the backup is the only way back. Then write down the current value of every variable in the retired variables table that your deployment sets. Several of those values are not stored anywhere else and the new engine does not read them, so this list is what you will re-enter later. A migration also deletes the seeded demo-agent where it was never used (open-source migration 0091, managed-services migration 0163); one with conversations, memories, knowledge, scheduled tasks, secrets or edits stays.
2. Move to Node 24, and let the images write their volumes. The engine and the panel now run on Node 24. The published images carry it, so a deployment that uses them needs nothing; if you run from source or build your own images, move to Node 24 (.nvmrc names it). Node 22 is no longer tested. Both images now run as the unprivileged node user (uid 1000) and declare a Docker health check. A volume mounted on the engine’s /app/logs or /app/packages/engine/workspaces must be writable by that user, for example with chown -R 1000:1000 on the volume, or the engine cannot write its log file or the conversation workspaces.
3. Move off custom S3 endpoints. The s3_endpoint agent secret is no longer read, and a migration deletes every stored value (open-source migration 0081, managed-services migration 0117). An agent that wrote to MinIO, Cloudflare R2 or another S3-compatible service falls back to AWS addressing, so its uploads and attachment reads fail. Move those buckets to AWS S3 first and configure each agent with s3_bucket_name, aws_region and static AWS keys (step 8). This release has no supported replacement for custom endpoints. See Attachment storage.
4. Give the engine a public address for Telegram and Slack. Both channels now receive messages as webhooks, like WhatsApp, instead of opening a connection out of the engine. A deployment that runs either one needs a public HTTPS address (BASE_URL, or the public address in Installation policies) and a proxy that sends /webhooks/* to the engine with no sign-in in front of it. Behind a proxy, set TRUST_PROXY to the number of hops, so that webhook rate limits see the sender’s address instead of the proxy’s. Where dev mode is used with managed services, /dev-mode/socket must reach the engine as a WebSocket too. The full list of paths is in the reverse proxy checklist.
5. Add the extracted tool plugins to your image. The GitHub, Render, HubSpot, Markdown-to-PDF and file-upload tools no longer ship in the core image. If an agent uses one of them, add its plugin before you build the new image (or list it in PLUGIN_DIRS in development). The HubSpot and extra plugins are public: clone the release tag that matches 1.2.0 into the engine’s plugin directory and rebuild, as each README shows.
git clone --depth 1 --branch v1.1.1 \
https://github.com/polyant-ai/polyant-hubspot-plugin.git packages/engine/src/plugins/hubspot
git clone --depth 1 --branch v1.1.0 \
https://github.com/polyant-ai/polyant-extra-plugin.git packages/engine/src/plugins/extraThe GitHub and Render tools have no open-source plugin. (managed services) An organization can instead install a plugin published on the marketplace, which needs no image rebuild (step 12).
| Old tool | New tool | Plugin |
|---|---|---|
hubspotContact, hubspotDeal, hubspotTicket, hubspotNote, hubspotFile, hubspotSendEmail, hubspotCreateTask, hubspotMeeting, hubspotGetCompany | hubspot:contact, hubspot:deal, hubspot:ticket, hubspot:note, hubspot:file, hubspot:sendEmail, hubspot:createTask, hubspot:meeting, hubspot:getCompany | polyant-hubspot-plugin v1.1.1 |
ghIssue, ghPR, gitCloneRepo | github:issue, github:pr, github:cloneRepo | no open-source plugin |
markdownToPdf | extra:markdownToPdf | polyant-extra-plugin v1.1.0 |
fileUpload | extra:fileUpload | polyant-extra-plugin v1.1.0 |
renderService | render:renderService | no open-source plugin |
verifyDocument | no replacement | |
claudeCode (managed services) | no replacement |
A migration renames the old catalogue entries in place (open-source migration 0087, managed-services migration 0138; for fileUpload, open-source migration 0089, managed-services migration 0150), so every agent keeps the tools it had enabled and every skill keeps its tool links and its list of required tools, under the new names. With managed services the same migration rewrites the tool names inside governance policies and their per-agent overrides. Until the plugin is installed the agent simply does not get the tool; once it is, the tool works again with nothing to re-enable. An agent bundle exported from an older version that names an old tool enables the new one on import.
The tools without a replacement are different: the first boot removes them, and every other tool no loaded plugin provides, from the agents that had them enabled, and logs their names once. To see beforehand which agents and skills use them:
SELECT i.slug AS agent, t.name AS tool
FROM instance_tools AS it
JOIN instances AS i ON i.id = it.instance_id
JOIN tools AS t ON t.id = it.tool_id
WHERE t.name IN ('claudeCode', 'verifyDocument')
ORDER BY i.slug, t.name;
SELECT s.slug AS skill, t.name AS tool
FROM skill_tools AS st
JOIN skills AS s ON s.id = st.skill_id
JOIN tools AS t ON t.id = st.tool_id
WHERE t.name IN ('claudeCode', 'verifyDocument')
ORDER BY s.slug, t.name;An installation that already ran a development build without the extracted tools and without the rename migration has lost those enablements, because the migration finds no old rows to rename. Re-enable the tools on each affected agent’s Tools tab after installing the plugins, and update any skill that still names an old tool.
6. Find the agents that use fileUpload without aws_region. extra:fileUpload requires aws_region, with no fallback to the deployment’s AWS_REGION, and so does attachment storage (step 7). An agent that lacks it stops seeing the tool with no error. With managed services the organization can share it together with the bucket name; the query below looks only at the agents’ own secrets, so an agent that inherits the region from its organization can be skipped. This query lists the agents to check:
SELECT i.slug
FROM instance_tools AS it
JOIN instances AS i ON i.id = it.instance_id
JOIN tools AS t ON t.id = it.tool_id
WHERE t.name = 'fileUpload'
AND NOT EXISTS (
SELECT 1 FROM instance_secrets AS s
WHERE s.instance_id = i.id AND s.key = 'aws_region'
);The same region rule applies to Bedrock: an agent with no AWS region configured, neither its own nor (with managed services) its organization’s, is refused, on chat and on embeddings. With managed services the region then lives in the Bedrock connection the upgrade creates from those secrets (step 22), so set it before upgrading or rotate the connection afterwards.
7. Decide which agents keep the files users send. Storing inbound attachments is now a per-agent switch, Store attachments in the agent’s Advanced section, and a migration adds it turned off on every agent (open-source migration 0088, managed-services migration 0139). With it off, the model still sees each file in the turn; the file is just not kept. Turned on, an agent with object storage configured stores every inbound attachment under attachments/<agent slug>/ in its bucket; an IAM policy limited to another prefix must allow that one.
Deleting a conversation or an agent now also deletes the files stored for it, and with managed services so does a retention purge. That needs s3:DeleteObject on the attachments/ prefix for the agent’s access key; a refused delete is logged, leaves the files in place and never blocks the database delete. An opt-out does not delete them. Every call to an agent’s bucket gives up after 5 seconds to connect or 60 seconds in all.
Earlier versions stored attachments in the deployment’s bucket (PLATFORM_S3_BUCKET). This release reads them only from each agent’s own bucket, so the conversation view can no longer open the files left there. The keys did not change: copy an agent’s prefix into its bucket and turn Store attachments on for that agent to make them readable again.
aws s3 sync "s3://<platform bucket>/attachments/<agent slug>/" "s3://<agent bucket>/attachments/<agent slug>/"8. Prepare static keys for every agent that used the task role. The s3_use_task_role secret is no longer read: an agent’s bucket is reached only with aws_access_key_id and aws_secret_access_key, by extra:fileUpload and by attachment storage alike, never with the deployment’s own AWS identity. An agent that relied on the task role stops uploading after the upgrade, and each attempt logs that s3_use_task_role is no longer supported and what to set instead. For each affected bucket, create an access key that may write and read where extra:fileUpload stores files and write, read and delete under attachments/. This query lists the agents:
SELECT i.slug
FROM instance_secrets AS s
JOIN instances AS i ON i.id = s.instance_id
WHERE s.key = 's3_use_task_role';9. Review agents with no pinned model. The OpenAI fast, standard and heavy tiers now resolve to gpt-6-luna, gpt-6-sol and gpt-6-astra, and the Bedrock standard and heavy tiers to Amazon Nova Pro and OpenAI gpt-oss 120B. Agents with an explicit model keep it, but every unpinned conversation and background job changes model and price. The Anthropic fast tier moves from Claude Haiku 4.5 to Claude Haiku 5.5, and it runs the background work (titles, memory, summaries) of every Anthropic agent, pinned or not, so those jobs change model and price too. On Bedrock, Nova Pro does not reason, so an unpinned agent with thinking on stops reasoning with no error, and with managed services the semantic governance gates now classify on gpt-oss 120B: re-run the governance evaluations you rely on. Pin a model where the change is not wanted, and with managed services review the agent’s spend cap before deploying.
10. Plan for the migrations that rewrite large tables. All pending migrations run in one transaction, and the image migrates before the engine listens, so they must finish inside the time the platform waits for a healthy task. If the platform replaces the task, the transaction rolls back and the upgrade has done nothing. For a heavy upgrade, run the migrations on their own first (step 13), so the deployment finds nothing left to apply. Two migrations grow with your data:
- The conversation counters migration (open-source
0086, managed-services0134) fills each conversation’s message counters from history and adds two indexes, one of them onconversation_messages. Writes to conversations and messages wait while it runs: on a test database with 2 million messages it took 8 seconds. On a large installation, schedule the deploy for a quiet moment, or build the two indexes beforehand without blocking, and the migration skips them:
ALTER TABLE conversations ADD COLUMN IF NOT EXISTS last_message_at timestamptz;
CREATE INDEX CONCURRENTLY IF NOT EXISTS "idx_conversations_instance_last_message"
ON "conversations" ("instance_id", "last_message_at");
CREATE INDEX CONCURRENTLY IF NOT EXISTS "idx_conversation_messages_conversation_created"
ON "conversation_messages" ("conversation_id", "created_at");- (managed services) Migration
0124rewrites everyai_logsrow and builds three indexes while holding locks on tables the engine reads. On a large table this takes minutes. Do the heavy part on the running 1.1 database first; the migration skips what already exists, and the 1.1 engine ignores the new columns:
ALTER TABLE ai_logs ADD COLUMN IF NOT EXISTS provider_connection_id uuid;
ALTER TABLE ai_logs ADD COLUMN IF NOT EXISTS contact_id text;
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_ai_logs_provider_connection
ON ai_logs (provider_connection_id);
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_ai_logs_instance_contact_created
ON ai_logs (instance_id, contact_id, created_at);Then run the backfill in batches, repeating until it updates no row:
UPDATE ai_logs AS log
SET contact_id = log.conversation_id
FROM conversations AS conversation
WHERE log.id IN (
SELECT l.id FROM ai_logs l
JOIN conversations c ON c.conversation_id = l.conversation_id
WHERE l.contact_id IS NULL
AND c.channel NOT IN ('room', 'scheduled', 'agent', 'webhook')
LIMIT 50000
)
AND conversation.conversation_id = log.conversation_id;If a CREATE INDEX CONCURRENTLY is interrupted, it leaves an invalid index that IF NOT EXISTS would still skip: drop it with DROP INDEX CONCURRENTLY and run the statement again.
Dashboards and other analytics reads now use a second, smaller connection pool, so each engine process opens up to 3 more connections to PostgreSQL (POSTGRES_ANALYTICS_POOL_MAX). Check the server’s max_connections if it is tight.
11. (managed services) List the retention policies that rely on the old default. Run this on the 1.1 database and keep the result with the backup:
SELECT instance_id, enabled, retention_days
FROM instance_retention_policies
WHERE retention_days IS NULL;Step 14 uses it. Once migration 0113 has run, the database can no longer tell these rows apart from policies that chose 15 days.
Also record which policies are on today. The upgrade switches every policy off (step 19) and does not remember which ones were enabled:
SELECT instance_id, retention_days
FROM instance_retention_policies
WHERE enabled = true;12. (managed services) Let the engine reach the plugin marketplace. Organizations can now install plugins at runtime from the official marketplace, https://marketplace.polyant.ai, whose signing key ships with the engine. The engine fetches the catalogue and re-verifies every selected release at each boot, so it needs outbound HTTPS to that origin; set PLUGIN_MARKETPLACE_OFFICIAL_ENABLED=false to switch it off. Plugins already baked into the image keep working through their existing per-organization grants. See Plugins.
During the upgrade window
13. (managed services) Stop the old engine before the new one migrates.
Warning. This upgrade is not rolling. Stop every 1.1 engine before the new image applies its migrations, and do not run a rolling or blue/green deployment. An organization or workspace that a 1.1 engine creates after the migrations have run gets no provider admission, so all its agents are refused every provider and no later migration repairs them. A 1.1 engine saving a retention policy with no window also fails. On ECS, set the service’s desired count to zero, wait for the task to stop, then deploy. There is no rollback past these migrations except restoring the backup.
To run the migrations on their own, with the same environment the engine gets:
docker run --rm --env-file .env --entrypoint node <engine-image> \
packages/engine/dist/database/migrate.jsIt prints Migrations applied successfully. and exits. The release migrations run through 0093_user_onboarding on the open-source build and 0165_user_onboarding with managed services. On ECS, register a task definition revision whose container sets entryPoint to ["node"] and command to ["packages/engine/dist/database/migrate.js"], and run it once. From source, npm run db:migrate does the same. The same one-off run works on the open-source build.
14. Carry custom retention windows over before the first boot.
Warning. The first analytics cleanup runs about 30 seconds after the new engine starts, and deletes logs, traces, tool audit records, hook executions, task runs and completed event backlog older than the installation’s analytics retention, which is 90 days until someone sets it. With managed services, migration
0113also writes 15 days onto every retention policy that stated no window. Neither old value (ANALYTICS_RETENTION_DAYS,RETENTION_DEFAULT_DAYS) is stored in the database. If either was customized, starting the new engine without carrying it over can delete data earlier than intended, and deleted data does not come back.
If neither variable was changed (90 and 15 days), skip this step. Otherwise, run the migrations on their own with the engine stopped (step 13), then, before starting the new engine:
- if
ANALYTICS_RETENTION_DAYSwas not 90, runUPDATE platform_settings SET analytics_retention_days = <your value>;(both builds); - (managed services) if
RETENTION_DEFAULT_DAYSwas not 15, setretention_dayson the policies you listed in step 11 to that value.
If migration 0113 has already run and you have no list from step 11, use the pre-upgrade backup to find the rows; do not guess.
15. Start the new engine. At boot it logs one warning for each retired variable it still finds set, naming where the value lives now. Keep that log: it is the checklist for step 17. (managed services) This first start also binds the provider roles of the agents with a voice channel, which the migrations cannot do because the voice settings are stored encrypted (step 22). On SIGTERM the engine now waits up to ten seconds for webhook turns already accepted; keep the container’s stop timeout above that (the ECS default is 30 seconds).
16. Point Slack at the webhook. For each Slack app, switch Socket Mode off, and under Event Subscriptions set the Request URL to <public address>/webhooks/slack/<agent slug>. The signing secret the channel already holds verifies the requests; the app-level token is no longer used. Telegram needs nothing by hand: the engine registers the webhook when the channel starts, and Telegram keeps undelivered updates for 24 hours, so messages sent while the address was unreachable still arrive.
After the first boot
17. Re-enter the settings that left the environment. Use the list from step 1 and the boot warnings from step 15. The full mapping is in the retired variables table; these are the ones that need action:
Warning. (managed services) Re-enter the mail relay.
SMTP_HOST,SMTP_PORT,SMTP_SECURE,SMTP_USER,SMTP_PASSWORD,MAIL_FROMandPANEL_URLare not read as a fallback, so after the upgrade the installation has no mailer at all until a platform admin enters one at Admin Console → Installation policies → Outbound mail. Until then invitations are created but not delivered, and password-reset requests send nothing. Do it in the upgrade window. See Installation policies.
Warning. (managed services) Re-enter your sign-in domain restriction.
AUTH_ALLOWED_DOMAINandAUTH_ALLOWED_DOMAINSare replaced by each organization’s SSO domains, which start empty, and an empty list restricts nothing. If you used the variables to restrict sign-in, enter the domains for each organization at Admin Console → Organizations → SSO domains before the upgrade window closes. Nobody who could sign in before loses access.
- Limits and timeouts. The live-stream caps, the rate-limit window and limit, the agent-to-agent and MCP timeouts, the scheduler’s orphan grace and default run deadline, and (managed services) the conversation-list timeout are now Installation policies: Admin Console → Installation policies with managed services, Settings → General on open source. They keep their old defaults, so a deployment that never set them needs nothing. If you set one to a different value, type the same number into the panel, or the upgrade quietly restores the default.
THROTTLE_ENABLEDstays an environment variable. - Per-agent behaviour. Time zone, locale, memory dedup threshold and the three message-coordination timings are now each agent’s Behaviour overrides. A deployment-wide time zone is
TZ. SettingTZalso moves every cron task that has no time zone of its own into that zone; to keep such a task on UTC, open it and save it, and the form fills inUTC. If you deploy with the AWS CDK stack, removedefaultInstanceIdandlocalefrom theappblock ofconfig.yaml;timezonestays and is passed asTZ. - (managed services) Knowledge document cap. A deployment that ran with more than 500 documents per agent sets
organizations.knowledge_max_docs_per_agentby SQL for each organization; there is no console field for it in this release. - (managed services) Default organization name. Rename the organization in the console.
DEFAULT_ORG_NAMEused to re-apply its value on every boot.
Then remove the retired variables from your environment. On the open-source build, also remove AUTH_MODE, which it no longer reads.
18. Make the extracted plugins reachable. The renamed tools come back on their own once the plugin’s tools reach the agent. On the open-source build that happens as soon as the plugin is in the image. With managed services, an image-baked plugin also needs a grant to the organization (Admin Console → Plugins → Image plugins) and an assignment to the agent’s workspace. Organizations that existed when the plugin first appeared are granted it once at boot, and migration 0158 assigns each extracted plugin to every workspace where an agent has one of its renamed tools enabled, so an image that bakes the plugins needs no further step. An organization or workspace created later needs an explicit grant and assignment. Review the uses of claudeCode and verifyDocument from step 5 by hand. (managed services) A governance policy that still names one of the two matches nothing and can be cleaned up at leisure.
19. (managed services) Re-enable each retention policy once its window is right. Migration 0135 switches every per-agent retention policy off and clears its next run, so after the upgrade no agent purges anything until someone enables its policy again. The schedule, the window and the governance flag of each policy are kept. Open each agent you recorded in step 11, check its window, and enable the policy. A window must now be a positive whole number of days: migration 0146 switches off any policy that held 0 or less and sets it to 15 days, and an agent bundle with a non-positive window is refused on import.
20. Set the bucket’s region and keys on each agent from steps 6 and 8. In the agent’s Advanced section, the Store attachments card holds the bucket, region and access keys. The region can also come, with managed services, from the organization’s shared credentials next to the bucket name; the access keys are always the agent’s own. Then delete the agent’s s3_use_task_role secret.
21. (managed services) Restore knowledge write access where an agent maintained a panel document. writeKnowledge now writes and appends only to documents the agent created itself. A document uploaded from the panel answers DOCUMENT_NOT_OWNED. If an agent kept such a document up to date, set its Knowledge Tool access to Full management, or let the agent re-create the document.
22. (managed services) Review provider connections and roles. Every provider key is now held by a named provider connection, and every task that calls a provider is a role bound to one: the conversation, the embedder, voice-note transcription, each part of a phone call and the organization’s library embedder. Provider keys stored as agent or organization secrets are no longer read. Migrations 0153 to 0156, and the first start for voice agents, carried what was there into connections and bindings, so each agent keeps the credential it used:
- the provider keys an organization shared became one organization connection per provider, lent to every workspace. Where the earlier Predefinita connection still held the same keys it was reused and lent to every workspace again; otherwise a “Provider (migrated)” connection was created;
- an agent’s own keys became a connection private to it;
- each role was bound with the precedence the secrets had, agent first, an existing chat connection winning.
Then check four things:
- Unbound roles in managed-only organizations. There an agent’s own key was already refused, and it stays refused: the role it served is left unbound rather than switched to the organization’s key. In such an organization an agent’s own ElevenLabs, Deepgram or Typesafe key became an organization connection named “Provider (migrated from agent slug)”, lent only to that agent’s workspace, with a
provider_connection.createrow in the management audit. The agent’s Status section names every role that cannot run; bind it in the agent’s Providers section. - The grants of the deployment’s AWS identity. A Bedrock credential with a region and no key used to sign with the engine’s own AWS identity. That now needs a platform admin’s grant per organization. The migration granted it to each organization that relied on it and wrote an
ambient_aws.org_grantrow for each: revoke it, from the organization’s Access tab in the Admin Console, for any organization that should use a Bedrock key of its own. - Admission for embeddings and speech-to-text. Provider admission used to bind the chat turn only. It now also binds embeddings (memories, knowledge documents, search) and voice-note transcription: an agent that embeds or transcribes on a provider its workspace does not admit is refused. Speech-to-text on Amazon Transcribe needs a Bedrock connection with an access-key pair and a region.
- Scripts and API clients. An agent created after the upgrade answers only once its roles are bound:
PUT /api/instances/<slug>/provider-roles/<role>with{ "connectionId": "<id>" }, forchat, forembeddingwhen memory or knowledge is on, forsttwhen transcription is on, and for each voice role its channel uses.PATCHandPOST /api/instancesrefuseproviderConnectionIdwith a400naming that route. The secret routes refuse provider keys with a400. An export no longer carries them, and an import answers aprovider_role_requiredwarning, with the role in itsrolefield, for each role to bind. A tool’sctx.apiKeyscarries the chat connection only.
The old secrets stay where they were, so a restored backup still matches them, and reach no tool, hook, plugin or dev-mode tool; running 1.1.x on the upgraded database is not supported. Once the upgrade is settled, delete an agent’s copies with DELETE /api/instances/<slug>/secrets/<key> and an organization’s with DELETE /api/organizations/<org>/secrets/<key>. The organization’s Provider credentials page is now Settings → Organization → Connections, and the old address redirects.
23. Update LangSmith filters. The conversation id in trace metadata is now conversation_id (it was oa_conversation_id on open source and ab_conversation_id with managed services). Change any LangSmith dashboard or filter built on the old key.
24. Check Telegram after the first boot. Each Telegram channel now has its own random webhook secret instead of one derived from the bot token. The engine mints it and registers the webhook again when the channel starts, so nothing is done by hand, but updates sent to the old registration fail verification until then. With more than one replica, restart all of them.
25. Update scripts that create agents. Web is now a channel. POST /v1/chat/completions and POST /api/instances/<slug>/chat/stream answer only while the agent’s web channel is on, and 404 otherwise. A migration switches it on for every agent that exists at the upgrade and keeps its authentication setting (open-source migration 0091, managed-services migration 0163), so existing agents keep answering. A new agent starts with Web off and requires its API key (migration 0090 on open source, 0162 with managed services), and enabling Web needs that key unless authentication is turned off after accepting the risk notice. A script that creates an agent and then calls it must save the agent’s key and enable the channel:
curl -X PUT "$POLYANT_URL/api/instances/<slug>/channels/web" \
-H "X-Polyant-Key: $POLYANT_KEY" -H "Content-Type: application/json" \
-d '{"config":{},"enabled":true}'Agent bundles are exported as format 1.3 on open source and 1.5 with managed services, and carry the Web state; an older bundle imports with Web on. A2A keeps its own switch. Scripts built on polyant-cli need version 0.9.0; with managed services, 0.7.0 and earlier do not work against this release.
26. Grant the Playground permission to custom roles that need it. The panel’s Playground now has its own route, POST /api/instances/<slug>/playground/stream, which uses the signed-in session and the agent.playground:use permission, so testing an agent needs neither its key nor its Web channel. A migration grants the permission to every built-in role, Viewer included (open-source migration 0092, managed-services migration 0164). (managed services) The same migration grants it to each custom role that holds agent:write; a custom role without agent:write whose holders should use the Playground needs it granted in the role editor.
Changes that need no action
createSkillis not enabled on new agents. Existing agents keep the tools they have; enablecreateSkillon a new agent that should write skills.- (managed services) Phone calls. The telephone voice channel is new and off until an agent’s Voice channel is set up. Before the first call, route
/webhooks/twilio/*to the engine with WebSocket upgrades and bind a connection to each voice role the call uses; see Channels → Voice. Spend caps cover only part of a call, and the engine limits neither concurrent calls nor call length: set usage limits at ElevenLabs, Deepgram and Typesafe and a concurrency limit on the Twilio number before publishing it. - (managed services) Personal API tokens. A dev token or an invitation created through a personal API token is now tied to it: revoking the token deletes its dev tokens and revokes its pending invitations, and neither can outlive the token’s expiry. Credentials issued before the upgrade carry no link. See Personal API tokens.
- Management API keys from the panel. On the open-source build an organization Owner now issues and revokes management keys from API keys in the sidebar; polyant-cli authenticates with them. Keys are now stored and checked as a SHA-256 digest: an existing key keeps working and is rewritten on first use, so a rollback to 1.1.x needs the keys used on 1.2.0 issued again.
- (managed services) Management API keys. A management API key can no longer create or edit management API keys or custom roles, even with
org:write, and it can revoke only itself. Do those from a signed-in session. Deleting an organization or a custom role, and changing the outbound mail relay or the installation settings, now also require a signed-in session and refuse management keys and personal tokens; a custom role that someone still holds answers409instead of being deleted. - First-access guidance. New users are offered a guided setup and interface tours, reopened from the Help menu in the header. Accounts that exist at the upgrade start with all of it dismissed (open-source migration
0093, managed-services migration0165). - (managed services) Semantic governance gates. They still fail open by default. A policy can now be set to fail closed, which is off for every existing policy.
- The engine’s public address.
BASE_URLis still read and is still what a fresh installation boots with. A public address stored in Installation policies wins where it is set, so a wrong webhook address can be corrected without a redeploy. With no address stored, nothing changes. - Retention policies. (managed services) A new policy must state its window; a request that omits it is refused. An agent bundle exported before this release with no window still imports, with the same 15-day window.
- Dependencies. axios is updated to 1.20.0, which fixes twelve high-severity advisories in 1.19.0.
Upgrading to 1.1.0
Some releases need an operator, not just a deploy. This one needs seven decisions, two of them in a specific order, and it carries 26 migrations. Take a database backup first.
Note. The managed-services build ships this release as
1.1.0-ee, the open-source build as1.1.0. Migration numbers on this page are the managed-services journal’s -0075through0100; the open-source journal numbers its own and does not carry the tables available only with managed services.
0. Audit POSTGRES_SSL before anything else. It used to accept any string and treat everything but the literal true as off, so POSTGRES_SSL=1, require, TRUE or yes all silently disabled TLS while reading as though they enabled it. The value is now validated: only true and false (and empty, meaning off) are accepted, and anything else exits at boot. If one of those values was set against managed Postgres, you were connecting unencrypted - rewrite it as true and treat the exposure accordingly. This is the one change that bites before the migrations even start.
Note the remaining caveat: true gives TLS without certificate verification. It defeats a passive listener, not an active man-in-the-middle. Verifying the chain needs a CA bundle this configuration does not take yet.
1. This is not a rolling upgrade - stop, then start. Migrations are applied at container start, so migration and code are atomic per container but not across replicas. Three changes make an overlapping deploy unsafe: the platform-admin role rename means an old replica classifies every platform admin as an ordinary user (intermittent 403s depending on which replica answers), the MCP and A2A columns are read unconditionally by new code (42703 on every agent read if the code starts first), and dropping users.role breaks every read of the users table in an old image. Use a stop-then-start or single-replica strategy; not blue/green, not a rolling update with overlap. The condition is transient and loses no data either way.
2. There are two points of no return, and neither has a down step. The first is the platform-admin rename: rolling back past it is worse than a failed forward deploy, because the old image compares against superadmin so nobody reaches the accounts API, while the is_platform_admin flag stays true and keeps granting the permission bypass - a half-privileged state. If you must go back, restore the role value first. The second is the drop of users.role, described under the migrations below. Treat everything from the rename onward as forward-only.
3. Force everyone to sign in again. The organization is stamped into the session token at sign-in and never refreshed, so anyone signed in across the upgrade keeps a token without it and is denied on every organization-scoped route for the life of that token (up to 24 hours). Rotate AUTH_SECRET as part of the deploy, or clear the sessions table. Without this, the panel appears broken for already-signed-in users.
4. Check who can reach what before you announce it. Three authorization changes take effect immediately:
- RBAC is enforced unconditionally. Expect denials that were previously silent passes, and verify each role can still do its job. The only way to shadow mode is
REMOVE_AUTHORIZATION_FILTER_FOR_TESTING=true, ignored underNODE_ENV=production. AUTH_MODE=alb-oidcmust becomeAUTH_MODE=sessionbefore deploying - the engine now refuses to boot on it. This includes infrastructure-as-code: a CDK stack that emitsAUTH_MODE=alb-oidcwhenever its auth prop is configured will crash-loop until the stack stops emitting it.- Member gained secret-write. Every existing Member can now write provider API keys and channel tokens and export an agent’s full configuration bundle. Secrets stay write-only through the API, but if that is wider than you want, review your Member assignments before upgrading: the change applies to every organization with no opt-out and no down migration, so narrowing it again means editing the role’s permissions by hand.
Accounts created after RBAC first shipped may hold no organization membership at all, since signing in no longer provisions one. A platform admin grants it from Members and roles - and that person must then sign out and back in, for the same reason as step 3.
5. Tell people their bookmarks are gone. Frontend URLs are tenant-scoped and the old flat paths are not redirected: /instances, /conversations/<id>, /playground, /members and friends return a 404, as do stale ?tab= values on an agent’s page. The canonical form is /organizations/<org>/workspaces/<workspace>/…. Point people at the organization dashboard and let them re-bookmark.
6. If you automate user creation, move off role. POST /api/users and PATCH /api/users/{id} take isPlatformAdmin: boolean and no longer return role. The alias is accepted on input for one release and never persisted or echoed back, so a script that sends role has one release to switch, while a script that reads it off a response is already broken by this release.
The migrations
Twenty-six, 0075 through 0100. None has a down step, one drops a column, and several reshape existing rows rather than adding to them - which is what makes the upgrade forward-only rather than merely long. These are the ones that need a decision or change what somebody can see; the rest are additive and need nothing from you.
| Migration | What it does |
|---|---|
| Governance catalogue per organization | A policy now belongs to the organization that owns it, so a catalogue shared across tenants is reshaped on upgrade |
| Default workspace per organization | ”Default workspace” becomes a per-organization notion rather than a deployment-wide one |
| Platform-admin role value | Rewrites the stored value to platform_admin (was superadmin). No down migration - the first point of no return |
| Permission tiers | Permissions carry a tier, so a workspace binding decides workspace-tier permissions instead of being overridden by an organization binding, and workspace administration gains its own permissions |
| Workspace skill/plugin assignment | Creates the two allow-list tables, and backfills them |
| Workspace-assignment permission | Grants workspace.assignment:manage to Owner and Admin |
| Channel disable reason | Adds the reason and timestamp an engine-disabled channel records |
| MCP server health | Adds the last-check time, outcome and error on each configured MCP server |
| AI log outcome | Adds outcome and error_kind to ai_logs, plus an index |
| Agent check dismissals | Creates the table behind silencing a Status check |
| Organization plugin read | Grants org.plugin:read to Owner and Admin |
Drop users.role | Reconciles any row where the column and the flag disagree, then drops the column |
conversations.updated_at index | Adds the index behind the organization-wide conversation list |
| Conversation-analysis indexes | Adds tool_audit_logs.message_id - nullable and not backfilled, so a historical tool row links to its conversation but not to its turn - plus three indexes for the conversation list’s per-request aggregates |
| Configurable memory scope | Adds the per-agent memory scope and its narrowing key |
email_threads | Creates the Message-ID index behind the email channel |
Three of them deserve a decision rather than a read-through:
The allow-list backfill is what makes the upgrade a non-event. Nothing was filtered per workspace before, so without it every agent would lose every skill and plugin the instant the migration applied. The backfill gives each workspace that exists at that moment everything its organization holds. Two consequences persist afterwards, and both read as bugs the first time: a workspace created later starts empty, and a new organization-level grant reaches no workspace. See What a workspace may draw on.
The ai_logs index will lock writes while it builds. Migrations run in a single transaction, so the index cannot be created concurrently. On a deployment where ai_logs is large, that is a write stall on the busiest table for the duration. The two column additions are metadata-only on modern Postgres and are not the problem. If the stall is unacceptable, skip that one statement and build the index out of band afterwards - the columns work without it, only the Status page’s failure-rate queries are slower until it exists.
Dropping users.role is the second point of no return, and it is not rolling-safe. The column and the is_platform_admin flag were two representations of one fact, the second derived from the first with nothing keeping them in sync; from here the flag is the sole authority. Before the drop the migration promotes any row where the column says platform admin and the flag says otherwise - a row someone had promoted with a direct UPDATE users SET role, which would otherwise lose every power it has, silently, the moment the column disappeared. Two consequences: old code that finds the column missing fails on every SELECT of the users table, so this is the stop-then-start of step 1 and not an overlapping deploy; and the migration is not re-runnable, since only the DROP is guarded by IF EXISTS.
One permission change narrows what Member and Viewer see. org.plugin:read splits the organization’s plugin inventory out of org:read, so Member and Viewer stop seeing it. Nothing else moves off org:read. If a custom role or an API key depended on reading that inventory, grant it the new key explicitly.