Skip to main content

Changelog

4.0

4.0.0

Release date: 28.08.2026

Breaking Changes

For more details, see the Upgrade section.

  • Dropped support for Node.js 20. io.Manager now requires Node.js 22 LTS or 24 LTS.

  • Dropped support for MongoDB 6. io.Manager now requires MongoDB 7 or later.

  • The io.Manager Server is now compatible with the io.Manager Admin UI only when their major and minor versions match. For example, io.Manager Server 4.0.x is compatible with io.Manager Admin UI 4.0.x (any patch), but io.Manager Server 4.0.0 isn't compatible with io.Manager Admin UI 4.1.0. Make sure to upgrade both packages together so that their major and minor versions stay aligned. Compatibility with @interopio/manager-api, io.Connect Desktop, and io.Connect Browser is unchanged.

  • Removed the custom-store extension point. The "custom" value for the type property of the store top-level key in the configuration object for initializing the io.Manager Server is no longer supported, along with the corresponding CustomStoreConfig configuration object. The store property now only accepts a MongoStoreConfig, a PostgreSQLStoreConfig, or an MSSQLStoreConfig object as a value. Existing deployments must switch to one of the three built-in database backends.

  • Removed the usersStore property from the MongoStoreConfig, PostgreSQLStoreConfig, and MSSQLStoreConfig configuration objects. A separate users store can no longer be supplied alongside one of the built-in database backends.

  • Removed the StoreBase, UsersStoreBase, and BLOBStoreBase abstract classes from the public API. Custom implementations of these classes can no longer be supplied to the io.Manager Server. The methods previously declared on UsersStoreBase (getUser, getAllUsers, addOrUpdateUser, removeUser, removeAllUsers, and setUserOtherField) are now part of the built-in store implementations directly.

  • The GET /api/crashes/{id}/dump endpoint of the io.Manager Server REST API now requires authentication and the IO_MANAGER:CRASHES:READ permission group. External callers that download crash dumps must be updated accordingly, or switched to the new Crashes.getDump(id): Promise<Blob> method on @interopio/manager-api, which reuses the client's existing authentication and returns the dump as a Blob.

  • Changed the GroupsService interface used when providing a custom Groups service via the groups_service configuration property. The addGroup method now takes a Group object and returns the created group, the Group object now includes an optional expandsTo array, and the getSupportedFeatures method reports new capability flags for the added group operations and for removing all groups. Existing custom GroupsService implementations must be updated accordingly.

  • The POST /groups endpoint of the io.Manager Server REST API now returns status code 400 when a group with the same name already exists; previously, posting the name of an existing group succeeded without changing it. Creating a group is now strict on both POST /groups and the new POST /v2/groups/add. To create or update a group, use POST /v2/groups; to update an existing group, use POST /v2/groups/update.

New Features

  • Added the GET /api/v2/commands/{id}/file endpoint of the io.Manager Server REST API that responds with the command result file's own bytes.

  • Added the GET /api/v2/server/log endpoint of the io.Manager Server REST API that responds with a zip archive of every log file the logging configuration writes, including the rotated backups. Use the includeBackups query parameter to leave the rotated backups out, and the fileNames query parameter to limit the archive to specific log files.

  • Added product-aware sessions and command descriptions for io.Connect Desktop and io.Connect Browser.

  • Added product and productVersion database columns to the sessions table and a product database column to the commands_for_version table, whose uniqueness constraint now covers product and version together (for PostgreSQL and Microsoft SQL Server only).

⚠️ Note that users who don't use automatic schema migration must create the columns and re-key the existing rows manually by running the following scripts.

For PostgreSQL:

ALTER TABLE sessions
    ADD COLUMN IF NOT EXISTS product VARCHAR(255), -- The io.Connect product that opened the session.
    ADD COLUMN IF NOT EXISTS "productVersion" VARCHAR(255); -- Version of that product. Determines which commands the session supports.

UPDATE sessions
SET
    product = CASE
        WHEN glue->'core'->'platform'->>'version' IS NOT NULL
            OR glue->>'version' LIKE 'io.connect.browser-%'
            THEN 'ioCB'
        ELSE 'ioCD'
    END,
    "productVersion" = CASE
        WHEN glue->'core'->'platform'->>'version' IS NOT NULL
            THEN glue->'core'->'platform'->>'version'
        WHEN glue->>'version' LIKE 'io.connect.browser-%'
            THEN substring(glue->>'version' from 20)
        ELSE glue->>'version'
    END;

ALTER TABLE sessions
    ALTER COLUMN product SET NOT NULL,
    ALTER COLUMN "productVersion" SET NOT NULL;

ALTER TABLE commands_for_version
    ADD COLUMN IF NOT EXISTS product VARCHAR(255); -- The io.Connect product. Unique together with the version.

ALTER TABLE commands_for_version
    DROP CONSTRAINT IF EXISTS commands_for_version_version_unique;

-- Only rows that have not been re-keyed yet: product is derived from version while version
-- itself is rewritten, so a second pass would classify an already-re-keyed row as ioCD.
UPDATE commands_for_version
SET
    product = CASE
        WHEN version LIKE 'io.connect.browser-%' THEN 'ioCB'
        ELSE 'ioCD'
    END,
    version = CASE
        WHEN version LIKE 'io.connect.browser-%'
            THEN substring(version from 20)
        ELSE version
    END
WHERE product IS NULL;

ALTER TABLE commands_for_version
    ALTER COLUMN product SET NOT NULL;

ALTER TABLE commands_for_version
    ADD CONSTRAINT commands_for_version_product_version_unique
    UNIQUE (product, version);

For Microsoft SQL Server:

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[sessions]')
    AND name = 'product'
)
ALTER TABLE sessions ADD product NVARCHAR(255) NULL; -- The io.Connect product that opened the session.
GO

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[sessions]')
    AND name = 'productVersion'
)
ALTER TABLE sessions ADD "productVersion" NVARCHAR(255) NULL; -- Version of that product. Determines which commands the session supports.
GO

UPDATE sessions
SET
    product = CASE
        WHEN JSON_VALUE(glue, '$.core.platform.version') IS NOT NULL
            OR JSON_VALUE(glue, '$.version') LIKE 'io.connect.browser-%'
            THEN 'ioCB'
        ELSE 'ioCD'
    END,
    "productVersion" = CASE
        WHEN JSON_VALUE(glue, '$.core.platform.version') IS NOT NULL
            THEN JSON_VALUE(glue, '$.core.platform.version')
        WHEN JSON_VALUE(glue, '$.version') LIKE 'io.connect.browser-%'
            THEN SUBSTRING(
                JSON_VALUE(glue, '$.version'),
                20,
                LEN(JSON_VALUE(glue, '$.version'))
            )
        ELSE JSON_VALUE(glue, '$.version')
    END;

ALTER TABLE sessions ALTER COLUMN product NVARCHAR(255) NOT NULL;
ALTER TABLE sessions ALTER COLUMN "productVersion" NVARCHAR(255) NOT NULL;

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[commands_for_version]')
    AND name = 'product'
)
ALTER TABLE commands_for_version ADD product NVARCHAR(255) NULL; -- The io.Connect product. Unique together with the version.
GO

ALTER TABLE commands_for_version
    DROP CONSTRAINT IF EXISTS commands_for_version_version_unique;

-- Only rows that have not been re-keyed yet: product is derived from version while version
-- itself is rewritten, so a second pass would classify an already-re-keyed row as ioCD.
UPDATE commands_for_version
SET
    product = CASE
        WHEN version LIKE 'io.connect.browser-%' THEN 'ioCB'
        ELSE 'ioCD'
    END,
    version = CASE
        WHEN version LIKE 'io.connect.browser-%'
            THEN SUBSTRING(version, 20, LEN(version))
        ELSE version
    END
WHERE product IS NULL;

ALTER TABLE commands_for_version
    ALTER COLUMN product NVARCHAR(255) NOT NULL;

ALTER TABLE commands_for_version
    ADD CONSTRAINT commands_for_version_product_version_unique
    UNIQUE (product, version);
  • Added support for PostgreSQL 18. io.Manager now requires PostgreSQL 14, 15, 16, 17, or 18.

  • Added support for Microsoft SQL Server 2025. io.Manager now requires Microsoft SQL Server 2016 SP3, 2017, 2019, 2022, or 2025.

  • Added a dbConnectivityQuery property to the HealthEndpointsConfig object. Use this property to specify the SQL query executed by the database connectivity health check when using PostgreSQL or Microsoft SQL Server. Configure a read-only query that accesses the database objects and permissions required by your deployment. The query is executed by the GET /db-connectivity endpoint and any custom database connectivity endpoint. It defaults to "select 1".

Property Type Description
dbConnectivityQuery string SQL query executed by the database connectivity health check when using PostgreSQL or Microsoft SQL Server. Defaults to "select 1".

The following example demonstrates how to verify that the health check can access the users table:

import { start } from "@interopio/manager";

const config = {
    healthEndpoints: {
        dbConnectivityQuery: "select id from users where id = '__io_manager_health_check__'"
    }
};

const server = await start(config);

You can also use the following environment variable:

Environment Variable Description
API_HEALTH_ENDPOINTS_DB_CONNECTIVITY_QUERY SQL query executed by the database connectivity health check when using PostgreSQL or Microsoft SQL Server. Defaults to select 1.
API_HEALTH_ENDPOINTS_DB_CONNECTIVITY_QUERY="select id from users where id = '__io_manager_health_check__'"
  • Added a granular permission-group system to the io.Manager Server REST API. Endpoints are gated by per-resource, per-action permission groups (e.g. IO_MANAGER:APPS:READ, IO_MANAGER:LAYOUTS:WRITE) instead of a single admin group. For the full list of built-in permission groups and the default groups, see Authorization.

  • Added the auth_extra_groups top-level key in the configuration object for initializing the io.Manager Server. Use this property to define additional groups that expand into granular permission groups or other groups. The supplied entries are merged with the built-in default groups, which take precedence over an entry with the same name. For the full list of available permission groups and the default groups, see Authorization.

Property Type Description
auth_extra_groups Group[] List of additional groups. Accepts an array of Group objects.

The following example demonstrates how to configure custom groups:

import { start } from "@interopio/manager";

const config = {
    auth_extra_groups: [
        {
            name: "apps-manager",
            description: "Can read and write apps.",
            expandsTo: ["IO_MANAGER:APPS:READ", "IO_MANAGER:APPS:WRITE", "IO_MANAGER:SCHEMAS:READ"]
        },
        {
            name: "apps-layouts-manager",
            description: "Can read and write apps and layouts.",
            expandsTo: ["apps-manager", "IO_MANAGER:LAYOUTS:READ", "IO_MANAGER:LAYOUTS:WRITE"]
        },
        {
            name: "read-only",
            description: "Can read all resources.",
            expandsTo: ["IO_MANAGER:APPS:READ", "IO_MANAGER:AUDITS:READ", "IO_MANAGER:COMMANDS:READ", "IO_MANAGER:CRASHES:READ", "IO_MANAGER:FEEDBACKS:READ", "IO_MANAGER:GROUPS:READ", "IO_MANAGER:LAYOUTS:READ", "IO_MANAGER:MACHINES:READ", "IO_MANAGER:PREFS:READ", "IO_MANAGER:SCHEMAS:READ", "IO_MANAGER:SESSIONS:READ", "IO_MANAGER:SYSTEM:READ", "IO_MANAGER:SYSTEM_CONFIG:READ", "IO_MANAGER:USERS:READ"]
        }
    ]
};

const server = await start(config);

You can also use the following environment variable:

Environment Variable Description
API_AUTH_EXTRA_GROUPS JSON-encoded array of custom group definitions. Each entry must have a name, and may have an expandsTo array and a description.

The following example demonstrates how to configure custom groups via environment variables:

API_AUTH_EXTRA_GROUPS=[
    {
        "name": "apps-manager",
        "description": "Can read and write apps.",
        "expandsTo": ["IO_MANAGER:APPS:READ", "IO_MANAGER:APPS:WRITE", "IO_MANAGER:SCHEMAS:READ"]
    },
    {
        "name": "apps-layouts-manager",
        "description": "Can read and write apps and layouts.",
        "expandsTo": ["apps-manager", "IO_MANAGER:LAYOUTS:READ", "IO_MANAGER:LAYOUTS:WRITE"]
    }
]
  • Added the advancedLayouts top-level key in the configuration object for initializing the io.Manager Server. Use this property to switch io.Connect platform clients between legacy and advanced Layouts and to restrict who can save advanced Layouts with accessLevel set to "shared" or "public". When advancedLayouts.enabled is set to true, the server serves advanced Layouts; when false (default), it serves legacy Layouts. Using advanced Layouts requires io.Connect Desktop 10.5 (unreleased) or later as a platform client.
Property Type Description
enabled boolean If true, the io.Manager Server serves advanced Layouts to io.Connect platform clients instead of legacy Layouts. Defaults to false.
sharingRequiredGroup string Name of a group whose members are allowed to save advanced Layouts with accessLevel set to "shared" or "public". Membership in nested groups is honored. Saving advanced Layouts with accessLevel set to "private" is never restricted. When omitted, any authenticated user may share or publish advanced Layouts.

The following example demonstrates how to enable advanced Layouts and restrict Layout sharing to members of a "layout-sharers" group:

import { start } from "@interopio/manager";

const config = {
    advancedLayouts: {
        // Serve advanced Layouts to io.Connect platform clients.
        enabled: true,
        // Only members of the `layout-sharers` group will be allowed to save
        // advanced Layouts with `accessLevel` set to `"shared"` or `"public"`.
        sharingRequiredGroup: "layout-sharers"
    }
};

const server = await start(config);

You can also use the following environment variables:

Environment Variable Description
API_ADVANCED_LAYOUTS_ENABLED If true, the io.Manager Server serves advanced Layouts to io.Connect platform clients instead of legacy Layouts. Defaults to false.
API_ADVANCED_LAYOUTS_SHARING_REQUIRED_GROUP Name of a group whose members are allowed to save advanced Layouts with accessLevel set to shared or public. Membership in nested groups is honored. Saving advanced Layouts with accessLevel set to private is never restricted. When omitted, any authenticated user may share or publish advanced Layouts.

The following example demonstrates how to enable advanced Layouts and restrict Layout sharing to members of a layout-sharers group via environment variables:

API_ADVANCED_LAYOUTS_ENABLED=true
API_ADVANCED_LAYOUTS_SHARING_REQUIRED_GROUP=layout-sharers

The GET /v2/server/info endpoint response now includes an advancedLayoutsEnabled field. Use it to detect from io.Connect platform clients whether the connected io.Manager Server is serving advanced Layouts or legacy Layouts.

  • Added the /advancedLayouts endpoints to the io.Manager Server REST API. Use them to read, create, update, and delete advanced Layouts, and to check a Layout identity against the uniqueness rule before saving. The read endpoints require the IO_MANAGER:LAYOUTS:READ permission group and the write endpoints require IO_MANAGER:LAYOUTS:WRITE. Using advanced Layouts requires io.Connect Desktop 10.5 (unreleased) or later as a platform client. For the full request and response shapes, see the Swagger UI.
Endpoint Description
GET /advancedLayouts Lists advanced Layouts, with support for filtering, grouping, sorting, and paging.
GET /advancedLayouts/{id} Returns the advanced Layout with the given id.
POST /advancedLayouts Creates an advanced Layout, or updates it if one with the same id already exists.
POST /advancedLayouts/add Creates an advanced Layout. Returns 400 when one with the same name and type already exists, so an existing Layout is never overwritten.
POST /advancedLayouts/update Updates the advanced Layout with the given id. Returns 404 when no Layout with that id exists.
POST /advancedLayouts/check-conflict Reports whether saving a Layout with the given name, type, and access level would collide with an existing Layout, before the save is submitted.
DELETE /advancedLayouts/{id} Deletes the advanced Layout with the given id.
  • Added a one-time migration of legacy Layouts to advanced Layouts, available only when advanced Layouts are enabled. Use the new GET /advancedLayouts/migration/status endpoint to review the migration status, the new GET /advancedLayouts/migration/layouts endpoint to review the legacy Layouts a run would migrate and the advanced access each will receive, and the new POST /advancedLayouts/migration/run endpoint to run the migration. All three endpoints require the IO_MANAGER:LAYOUTS:WRITE permission group. The migration can be completed only once - a run that leaves nothing to migrate and reports no failures locks it permanently.

  • Added a migration property to the advancedLayouts configuration object. Use this property to control how the Layout migration runs.

Property Type Description
claimStaleness number Time in milliseconds after which an abandoned Layout migration run may be taken over by a new run. Must be a finite number greater than zero. Defaults to 600000 (10 minutes).
pageSize number Number of legacy Layouts a migration run reads and writes at a time. Lower values reduce the run's memory use and higher values reduce the number of database round trips. Must be an integer greater than zero. Defaults to 1000.

You can also use the following environment variables:

Environment Variable Description
API_ADVANCED_LAYOUTS_MIGRATION_CLAIM_STALENESS Time in milliseconds after which an abandoned Layout migration run may be taken over by a new run. Must be a finite number greater than zero. Defaults to 600000 (10 minutes).
API_ADVANCED_LAYOUTS_MIGRATION_PAGE_SIZE Number of legacy Layouts a migration run reads and writes at a time. Lower values reduce the run's memory use and higher values reduce the number of database round trips. Must be an integer greater than zero. Defaults to 1000.
  • Added a layouts_advanced database table for storing advanced Layouts (for PostgreSQL and Microsoft SQL Server only).

⚠️ Note that users who don't use automatic schema migration must create the layouts_advanced table manually by running the following scripts.

For PostgreSQL:

-- Layouts saved by an io.Connect platform, with per-entity access control.
CREATE TABLE IF NOT EXISTS layouts_advanced
(
    layouts_advanced_id SERIAL PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    id VARCHAR(255) NOT NULL, -- Public identifier of the layout. Unique.
    type VARCHAR(255) NOT NULL, -- Kind of layout, for example 'Global' or 'Workspace'.
    name VARCHAR(255) NOT NULL, -- Name of the layout. Unique per type per owner while private, and unique per type across all owners once shared or public.
    disabled BOOLEAN NOT NULL, -- Whether the layout is withheld from all users.
    "accessLevel" VARCHAR(255) NOT NULL, -- Who can reach the layout: 'private', 'shared' or 'public'.
    "owner" VARCHAR(255) NOT NULL, -- User the layout belongs to.
    "accessInfo" JSON NULL,
    definition JSON NOT NULL,
    "createdBy" VARCHAR(255) NOT NULL, -- User that created the layout.
    "createdOn" BIGINT NOT NULL, -- Creation time. Epoch milliseconds (UTC).
    "lastModifiedBy" VARCHAR(255) NULL, -- User that last modified the layout.
    "lastModifiedOn" BIGINT NULL, -- Time of the last modification. Epoch milliseconds (UTC).
    CONSTRAINT layouts_advanced_id_unique UNIQUE (id)
);

CREATE UNIQUE INDEX IF NOT EXISTS layouts_advanced__name__type__owner__unique
    ON layouts_advanced (name, type, owner)
    WHERE "accessLevel" = 'private';

CREATE UNIQUE INDEX IF NOT EXISTS layouts_advanced__name__type__nonprivate__unique
    ON layouts_advanced (name, type)
    WHERE "accessLevel" IN ('shared', 'public');

For Microsoft SQL Server:

-- Layouts saved by an io.Connect platform, with per-entity access control.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='layouts_advanced' AND xtype='U')
CREATE TABLE layouts_advanced
(
    layouts_advanced_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    id NVARCHAR(255) NOT NULL, -- Public identifier of the layout. Unique.
    type NVARCHAR(255) NOT NULL, -- Kind of layout, for example 'Global' or 'Workspace'.
    name NVARCHAR(255) NOT NULL, -- Name of the layout. Unique per type per owner while private, and unique per type across all owners once shared or public.
    disabled BIT NOT NULL, -- Whether the layout is withheld from all users. Boolean, stored as 0 or 1.
    accessLevel NVARCHAR(255) NOT NULL, -- Who can reach the layout: 'private', 'shared' or 'public'.
    owner NVARCHAR(255) NOT NULL, -- User the layout belongs to.
    "accessInfo" NVARCHAR(MAX) NULL, -- Users and groups granted access, with the level granted to each. Present only for shared layouts. JSON, stored as text.
    definition NVARCHAR(MAX) NOT NULL, -- The layout payload as saved by the io.Connect platform. JSON, stored as text.
    "createdBy" NVARCHAR(255) NOT NULL, -- User that created the layout.
    "createdOn" BIGINT NOT NULL, -- Creation time. Epoch milliseconds (UTC).
    "lastModifiedBy" NVARCHAR(255) NULL, -- User that last modified the layout.
    "lastModifiedOn" BIGINT NULL, -- Time of the last modification. Epoch milliseconds (UTC).
    CONSTRAINT layouts_advanced_id_unique UNIQUE (id)
);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'layouts_advanced__name__type__owner__unique' AND object_id = OBJECT_ID('layouts_advanced'))
CREATE UNIQUE INDEX layouts_advanced__name__type__owner__unique
    ON layouts_advanced (name, type, owner)
    WHERE accessLevel = 'private';

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'layouts_advanced__name__type__nonprivate__unique' AND object_id = OBJECT_ID('layouts_advanced'))
CREATE UNIQUE INDEX layouts_advanced__name__type__nonprivate__unique
    ON layouts_advanced (name, type)
    WHERE accessLevel IN ('shared', 'public');
  • Added an others database column to the users table (for PostgreSQL and Microsoft SQL Server only).

⚠️ Note that users who don't use automatic schema migration must create the others column manually by running the following scripts.

For PostgreSQL:

ALTER TABLE users
    ADD COLUMN IF NOT EXISTS others JSONB NULL; -- Per-user timestamps that do not have a column of their own. JSON.

For Microsoft SQL Server:

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[users]')
    AND name = 'others'
)
ALTER TABLE users
    ADD others NVARCHAR(MAX) NULL; -- Per-user timestamps that do not have a column of their own. JSON, stored as text.
  • Added description and expandsTo database columns to the groups table (for PostgreSQL and Microsoft SQL Server only).

⚠️ Note that users who don't use automatic schema migration must create the description and expandsTo columns manually by running the following scripts.

For PostgreSQL:

ALTER TABLE groups
    ADD COLUMN IF NOT EXISTS description TEXT NULL, -- Description of the group.
    ADD COLUMN IF NOT EXISTS "expandsTo" VARCHAR(255)[] NOT NULL DEFAULT '{}'::varchar[]; -- Groups this group also grants membership of. Empty when it expands into no others. Array of strings.

For Microsoft SQL Server:

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[groups]')
    AND name = 'description'
)
ALTER TABLE groups
    ADD description NVARCHAR(MAX) NULL; -- Description of the group.

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[groups]')
    AND name = 'expandsTo'
)
ALTER TABLE groups
    ADD expandsTo NVARCHAR(MAX) NOT NULL CONSTRAINT DF_groups_expandsTo DEFAULT '[]'; -- Groups this group also grants membership of. Empty when it expands into no others. JSON array of strings, stored as text.
  • Added a system_state database table for storing server-managed system state, such as the Layout-migration status (for PostgreSQL and Microsoft SQL Server only).

⚠️ Note that users who don't use automatic schema migration must create the system_state table manually by running the following scripts.

For PostgreSQL:

-- Server-wide state shared by every io.Manager instance, one row per named entry.
CREATE TABLE IF NOT EXISTS system_state
(
    system_state_id SERIAL PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    name VARCHAR(255) NOT NULL, -- Name of the entry. Unique.
    value JSONB NOT NULL, -- The entry value. JSON.
    version INT NOT NULL, -- Revision of the entry, raised on every write so concurrent instances cannot overwrite each other.
    CONSTRAINT system_state_name_unique UNIQUE (name)
);

For Microsoft SQL Server:

-- Server-wide state shared by every io.Manager instance, one row per named entry.
IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='system_state' AND xtype='U')
CREATE TABLE system_state
(
    system_state_id INT IDENTITY PRIMARY KEY, -- Primary key. Auto-assigned by the database and not used to identify the row outside it.
    name NVARCHAR(255) NOT NULL, -- Name of the entry. Unique.
    value NVARCHAR(MAX) NOT NULL, -- The entry value. JSON, stored as text.
    version INT NOT NULL, -- Revision of the entry, raised on every write so concurrent instances cannot overwrite each other.
    CONSTRAINT system_state_name_unique UNIQUE (name)
);
  • Added a migrated database column to the layouts table (for PostgreSQL and Microsoft SQL Server only). The Layout migration uses it to mark legacy Layouts that have already been migrated to advanced Layouts.

⚠️ Note that users who don't use automatic schema migration must create the migrated column manually by running the following scripts.

For PostgreSQL:

ALTER TABLE layouts
    ADD COLUMN IF NOT EXISTS migrated BOOLEAN NULL; -- Whether this layout has already been migrated to the layouts_advanced table.

For Microsoft SQL Server:

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[layouts]')
    AND name = 'migrated'
)
ALTER TABLE layouts
    ADD migrated BIT; -- Whether this layout has already been migrated to the layouts_advanced table. Boolean, stored as 0 or 1.
  • Added an optional configKeys query parameter to the GET /systemConfig endpoint of the io.Manager Server REST API. Use this parameter to narrow which config-file names appear in each entry's configs map, reducing the response size when only a subset of config files is needed.

  • Added a user-facing REST API surface at /v2/user/* to the io.Manager Server for io.Connect platform clients, consumed by the new ClientAPIV2 class in @interopio/manager-api.

  • Added the POST /v2/user/query-users and POST /v2/user/query-groups endpoints for looking up users and groups by name. Use them from io.Connect platform clients via the new io.manager.users.query() and io.manager.groups.query() methods.

  • Added the POST /v2/user/layouts/check-conflict and POST /v2/user/advancedLayouts/check-conflict endpoints for checking whether saving a Layout with a given identity would collide with an existing one. Use them from io.Connect platform clients via the new io.manager.layouts.checkConflict() method to prompt the user before submitting a save - for example, to ask whether to overwrite an existing Layout or to pick a different name - instead of catching a failure after the fact.

  • Added an admin-facing REST API surface at /v2/groups/* to the io.Manager Server for reading, creating, updating, and deleting stored groups, and for expanding a set of group names into the full set of groups and granular permission groups they map to. Read operations require the IO_MANAGER:GROUPS:READ permission group and write operations require the IO_MANAGER:GROUPS:WRITE permission group. The new surface is consumed by the new GroupsV2 class in @interopio/manager-api.

  • Added a transactions property to the MongoStoreConfig object. Use this property to control whether the io.Manager Server uses MongoDB multi-document transactions, which require MongoDB to be deployed as a replica set or a sharded cluster.

Property Type Description
transactions "disabled" | "required" | "autodetect" Whether to use MongoDB multi-document transactions. Set to "required" to prevent the server from starting when the MongoDB deployment doesn't support transactions, or to "disabled" to never use transactions. Defaults to "autodetect" - transactions are used when the deployment supports them, otherwise the server logs a warning and runs without transactions.

The following example demonstrates how to configure the transactions mode:

import { start } from "@interopio/manager";

const config = {
    store: {
        type: "mongo",
        connection: "mongodb://localhost:27017/my_db",
        // Refuse to start unless the MongoDB deployment supports transactions.
        transactions: "required"
    }
};

const server = await start(config);

You can also use the following environment variable:

Environment Variable Description
API_STORE_MONGO_TRANSACTIONS Whether to use MongoDB multi-document transactions. Defaults to autodetect.
API_STORE_MONGO_TRANSACTIONS=required
  • Added migrationRetries and migrationRetryDelayMs properties to the PostgreSQLStoreConfig and MSSQLStoreConfig objects. Use these properties to control how store initialization - creating the database, creating the schema and running the migration scripts - is retried after a transient failure, such as another server instance initializing the same database concurrently. The error of the final attempt is propagated, so a misconfigured store still fails to start.
Property Type Description
migrationRetries number How many times store initialization is retried after a transient failure. Defaults to 3.
migrationRetryDelayMs number The delay in milliseconds between retry attempts. Defaults to 1000.

You can also use the following environment variables:

Environment Variable Description
API_STORE_POSTGRESQL_MIGRATION_RETRIES How many times store initialization is retried after a transient failure. Defaults to 3.
API_STORE_POSTGRESQL_MIGRATION_RETRY_DELAY_MS The delay in milliseconds between retry attempts. Defaults to 1000.
API_STORE_MSSQL_MIGRATION_RETRIES How many times store initialization is retried after a transient failure. Defaults to 3.
API_STORE_MSSQL_MIGRATION_RETRY_DELAY_MS The delay in milliseconds between retry attempts. Defaults to 1000.
  • Added transactionRetries, transactionRetryDelayMs, and transactionIsolation properties to the PostgreSQLStoreConfig and MSSQLStoreConfig objects. Use transactionRetries and transactionRetryDelayMs to control how database transactions and standalone reads are retried after a transient failure, and transactionIsolation to choose the isolation level for the database transactions the server opens.
Property Type Description
transactionIsolation string The isolation level for the database transactions the server opens. Accepts "database-default", "read-committed", "repeatable-read" (PostgreSQL only), "snapshot" (Microsoft SQL Server only), or "serializable" as a value. "database-default" leaves the database's own default isolation level in effect. Defaults to "database-default".
transactionRetries number How many times a database transaction, or a standalone read, is re-run after a transient failure. Set to 0 to disable the retry. Defaults to 3.
transactionRetryDelayMs number The base delay in milliseconds between retry attempts. The actual delay is randomized and increases with each retry. Defaults to 100.

⚠️ Note that "snapshot" requires the database to have ALLOW_SNAPSHOT_ISOLATION enabled - otherwise, the server won't start.

You can also use the following environment variables:

Environment Variable Description
API_STORE_POSTGRESQL_TRANSACTION_ISOLATION The isolation level for the database transactions the server opens. Defaults to database-default.
API_STORE_POSTGRESQL_TRANSACTION_RETRIES How many times a database transaction, or a standalone read, is re-run after a transient failure. Defaults to 3.
API_STORE_POSTGRESQL_TRANSACTION_RETRY_DELAY_MS The base delay in milliseconds between retry attempts. The actual delay is randomized and increases with each retry. Defaults to 100.
API_STORE_MSSQL_TRANSACTION_ISOLATION The isolation level for the database transactions the server opens. Defaults to database-default.
API_STORE_MSSQL_TRANSACTION_RETRIES How many times a database transaction, or a standalone read, is re-run after a transient failure. Defaults to 3.
API_STORE_MSSQL_TRANSACTION_RETRY_DELAY_MS The base delay in milliseconds between retry attempts. The actual delay is randomized and increases with each retry. Defaults to 100.
  • Added two OpenTelemetry metrics for observing database resilience: "io_manager.db.transaction.duration" - the duration of the database transactions the server opens - and "io_manager.db.operation.retries" - the number of transient-failure retries of database transactions and standalone reads.

  • Added an OpenTelemetry trace span named "db.transaction" for each database transaction the server opens, recording an event for every transient-failure retry of that transaction.

Deprecated Endpoints

The following REST API endpoints have been deprecated:

Deprecated Endpoint New Endpoint
GET /commands/{id}/file Use GET /v2/commands/{id}/file instead.
GET /groups Use GET /v2/groups instead.
POST /groups Use POST /v2/groups/add for a strict insert, or POST /v2/groups for an upsert (create or update).
DELETE /groups/{name} Use DELETE /v2/groups/{name} instead.
GET /server/log Use GET /v2/server/log instead.

Improvements & Bug Fixes

  • On Microsoft SQL Server, all VARCHAR(255) columns are now NVARCHAR(255), so usernames, Layout names, and every other text field store the full Unicode range. Previously, a character outside the code page of the database collation, such as Cyrillic or Greek, was stored as ?.

⚠️ Note that users who don't use automatic schema migration must convert the columns manually by running the following script. Each unique constraint is dropped and recreated around the conversion, because a column can't be altered while a constraint references it.

ALTER TABLE app DROP CONSTRAINT app_name_unique;
ALTER TABLE audit DROP CONSTRAINT audit_id_unique;
ALTER TABLE blobs DROP CONSTRAINT blobs_id_unique;
ALTER TABLE commands DROP CONSTRAINT commands_id_unique;
ALTER TABLE commands_for_version DROP CONSTRAINT commands_for_version_product_version_unique;
ALTER TABLE crashes DROP CONSTRAINT crashes_id_unique;
ALTER TABLE feedback DROP CONSTRAINT feedback_id_unique;
ALTER TABLE groups DROP CONSTRAINT groups_name_unique;
ALTER TABLE layouts DROP CONSTRAINT layouts_id_unique;
ALTER TABLE layouts DROP CONSTRAINT layouts__name__type__owner__unique;
ALTER TABLE machines DROP CONSTRAINT machines_id_unique;
ALTER TABLE prefs DROP CONSTRAINT prefs_id_unique;
ALTER TABLE prefs DROP CONSTRAINT prefs__app__user__unique;
ALTER TABLE sessions DROP CONSTRAINT sessions_id_unique;
ALTER TABLE users DROP CONSTRAINT users_id_unique;

ALTER TABLE app ALTER COLUMN name NVARCHAR(255) NOT NULL;
ALTER TABLE app ALTER COLUMN [createdBy] NVARCHAR(255) NOT NULL;
ALTER TABLE app ALTER COLUMN [lastModifiedBy] NVARCHAR(255) NULL;
ALTER TABLE audit ALTER COLUMN id NVARCHAR(255) NOT NULL;
ALTER TABLE audit ALTER COLUMN [user] NVARCHAR(255) NULL;
ALTER TABLE audit ALTER COLUMN parent NVARCHAR(255) NULL;
ALTER TABLE audit ALTER COLUMN session NVARCHAR(255) NULL;
ALTER TABLE audit ALTER COLUMN server NVARCHAR(255) NULL;
ALTER TABLE audit ALTER COLUMN [entityType] NVARCHAR(255) NOT NULL;
ALTER TABLE audit ALTER COLUMN [entityId] NVARCHAR(255) NULL;
ALTER TABLE audit ALTER COLUMN [entityDisplayName] NVARCHAR(255) NULL;
ALTER TABLE audit ALTER COLUMN operation NVARCHAR(255) NOT NULL;
ALTER TABLE audit ALTER COLUMN [operationComment] NVARCHAR(255) NULL;
ALTER TABLE blobs ALTER COLUMN [type] NVARCHAR(255) NOT NULL;
ALTER TABLE blobs ALTER COLUMN id NVARCHAR(255) NOT NULL;
ALTER TABLE blobs ALTER COLUMN [fileName] NVARCHAR(255) NOT NULL;
ALTER TABLE commands ALTER COLUMN id NVARCHAR(255) NOT NULL;
ALTER TABLE commands ALTER COLUMN [user] NVARCHAR(255) NOT NULL;
ALTER TABLE commands ALTER COLUMN status NVARCHAR(255) NOT NULL;
ALTER TABLE commands ALTER COLUMN session NVARCHAR(255) NOT NULL;
ALTER TABLE commands ALTER COLUMN machine NVARCHAR(255) NOT NULL;
ALTER TABLE commands ALTER COLUMN [createdBy] NVARCHAR(255) NOT NULL;
ALTER TABLE commands ALTER COLUMN command NVARCHAR(255) NOT NULL;
ALTER TABLE commands ALTER COLUMN [commandParams] NVARCHAR(255) NULL;
ALTER TABLE commands ALTER COLUMN [resultType] NVARCHAR(255) NULL;
ALTER TABLE commands_for_version ALTER COLUMN version NVARCHAR(255) NOT NULL;
ALTER TABLE crashes ALTER COLUMN id NVARCHAR(255) NOT NULL;
ALTER TABLE crashes ALTER COLUMN [user] NVARCHAR(255) NULL;
ALTER TABLE feedback ALTER COLUMN id NVARCHAR(255) NOT NULL;
ALTER TABLE feedback ALTER COLUMN [user] NVARCHAR(255) NOT NULL;
ALTER TABLE feedback ALTER COLUMN session NVARCHAR(255) NOT NULL;
ALTER TABLE groups ALTER COLUMN name NVARCHAR(255) NOT NULL;
ALTER TABLE layouts ALTER COLUMN id NVARCHAR(255) NOT NULL;
ALTER TABLE layouts ALTER COLUMN [type] NVARCHAR(255) NOT NULL;
ALTER TABLE layouts ALTER COLUMN name NVARCHAR(255) NOT NULL;
ALTER TABLE layouts ALTER COLUMN [owner] NVARCHAR(255) NULL;
ALTER TABLE layouts ALTER COLUMN [createdBy] NVARCHAR(255) NOT NULL;
ALTER TABLE layouts ALTER COLUMN [lastModifiedBy] NVARCHAR(255) NULL;
ALTER TABLE machines ALTER COLUMN id NVARCHAR(255) NOT NULL;
ALTER TABLE machines ALTER COLUMN [user] NVARCHAR(255) NOT NULL;
ALTER TABLE machines ALTER COLUMN name NVARCHAR(255) NULL;
ALTER TABLE prefs ALTER COLUMN id NVARCHAR(255) NOT NULL;
ALTER TABLE prefs ALTER COLUMN app NVARCHAR(255) NOT NULL;
ALTER TABLE prefs ALTER COLUMN [user] NVARCHAR(255) NOT NULL;
ALTER TABLE sessions ALTER COLUMN id NVARCHAR(255) NOT NULL;
ALTER TABLE sessions ALTER COLUMN machine NVARCHAR(255) NOT NULL;
ALTER TABLE sessions ALTER COLUMN [user] NVARCHAR(255) NOT NULL;
ALTER TABLE sessions ALTER COLUMN [closeReason] NVARCHAR(255) NULL;
ALTER TABLE users ALTER COLUMN id NVARCHAR(255) NOT NULL;
ALTER TABLE users ALTER COLUMN email NVARCHAR(255) NULL;
ALTER TABLE users ALTER COLUMN password NVARCHAR(255) NULL;
ALTER TABLE users ALTER COLUMN [firstName] NVARCHAR(255) NULL;
ALTER TABLE users ALTER COLUMN [lastName] NVARCHAR(255) NULL;

ALTER TABLE app ADD CONSTRAINT app_name_unique UNIQUE (name);
ALTER TABLE audit ADD CONSTRAINT audit_id_unique UNIQUE (id);
ALTER TABLE blobs ADD CONSTRAINT blobs_id_unique UNIQUE (id);
ALTER TABLE commands ADD CONSTRAINT commands_id_unique UNIQUE (id);
ALTER TABLE commands_for_version ADD CONSTRAINT commands_for_version_product_version_unique UNIQUE (product, version);
ALTER TABLE crashes ADD CONSTRAINT crashes_id_unique UNIQUE (id);
ALTER TABLE feedback ADD CONSTRAINT feedback_id_unique UNIQUE (id);
ALTER TABLE groups ADD CONSTRAINT groups_name_unique UNIQUE (name);
ALTER TABLE layouts ADD CONSTRAINT layouts_id_unique UNIQUE (id);
ALTER TABLE layouts ADD CONSTRAINT layouts__name__type__owner__unique UNIQUE ([name], [type], [owner]);
ALTER TABLE machines ADD CONSTRAINT machines_id_unique UNIQUE (id);
ALTER TABLE prefs ADD CONSTRAINT prefs_id_unique UNIQUE (id);
ALTER TABLE prefs ADD CONSTRAINT prefs__app__user__unique UNIQUE ([app], [user]);
ALTER TABLE sessions ADD CONSTRAINT sessions_id_unique UNIQUE (id);
ALTER TABLE users ADD CONSTRAINT users_id_unique UNIQUE (id);
  • Added database indexes for every column a username is resolved by - users.id, layouts.owner, prefs.user, and machines.user - in both a case-folded and an exact form, so that both settings of username_case_sensitive use an index instead of scanning the table. On Microsoft SQL Server, the case-folded form is a persisted computed column with an index on it, because Microsoft SQL Server has no expression indexes. On MongoDB, the case-folded form is an index declaring the case-insensitive collation the comparison uses.

⚠️ Note that users who don't use automatic schema migration must create the indexes manually by running the following scripts. On MongoDB, the indexes are created by the server on startup and don't need any manual step.

For PostgreSQL:

CREATE INDEX IF NOT EXISTS users__id__lower ON users (lower(id));
CREATE INDEX IF NOT EXISTS layouts__owner__lower ON layouts (lower("owner"));
CREATE INDEX IF NOT EXISTS layouts__owner ON layouts ("owner");
CREATE INDEX IF NOT EXISTS prefs__user__lower__app ON prefs (lower("user"), app);
CREATE INDEX IF NOT EXISTS prefs__user ON prefs ("user");
CREATE INDEX IF NOT EXISTS machines__user__lower ON machines (lower("user"));
CREATE INDEX IF NOT EXISTS machines__user ON machines ("user");

For Microsoft SQL Server:

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[users]')
    AND name = 'id_lower'
)
ALTER TABLE users ADD id_lower AS LOWER(id) PERSISTED; -- The identifier, lowercased, for resolving a username when username_case_sensitive is false. Derived by the database.
GO

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[layouts]')
    AND name = 'owner_lower'
)
ALTER TABLE layouts ADD owner_lower AS LOWER([owner]) PERSISTED; -- The owner, lowercased, for resolving a username when username_case_sensitive is false. Derived by the database.
GO

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[prefs]')
    AND name = 'user_lower'
)
ALTER TABLE prefs ADD user_lower AS LOWER([user]) PERSISTED; -- The user, lowercased, for resolving a username when username_case_sensitive is false. Derived by the database.
GO

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[machines]')
    AND name = 'user_lower'
)
ALTER TABLE machines ADD user_lower AS LOWER([user]) PERSISTED; -- The user, lowercased, for resolving a username when username_case_sensitive is false. Derived by the database.
GO

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'users__id__lower' AND object_id = OBJECT_ID('users'))
    CREATE INDEX users__id__lower ON users (id_lower);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'layouts__owner__lower' AND object_id = OBJECT_ID('layouts'))
    CREATE INDEX layouts__owner__lower ON layouts (owner_lower);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'layouts__owner' AND object_id = OBJECT_ID('layouts'))
    CREATE INDEX layouts__owner ON layouts ([owner]);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'prefs__user__lower__app' AND object_id = OBJECT_ID('prefs'))
    CREATE INDEX prefs__user__lower__app ON prefs (user_lower, app);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'prefs__user' AND object_id = OBJECT_ID('prefs'))
    CREATE INDEX prefs__user ON prefs ([user]);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'machines__user__lower' AND object_id = OBJECT_ID('machines'))
    CREATE INDEX machines__user__lower ON machines (user_lower);

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'machines__user' AND object_id = OBJECT_ID('machines'))
    CREATE INDEX machines__user ON machines ([user]);

⚠️ Note that on Microsoft SQL Server, indexing a computed column makes the QUOTED_IDENTIFIER session option mandatory for every INSERT, UPDATE, and DELETE against the users, layouts, prefs, and machines tables. The io.Manager Server and current database drivers set it automatically, but tooling that writes to these tables directly may not - sqlcmd, for example, requires the -I flag. A session without it fails with "DELETE failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'".

  • Fixed a bug where the traceContext field of commands was returned as a JSON-encoded string instead of an object when io.Manager was running against Microsoft SQL Server, causing io.Connect platform clients to fail response validation and be unable to open a session when OpenTelemetry tracing was enabled.

  • Fixed an inconsistency where the resultAt, resultData, and traceContext fields of a not-yet-completed command serialized differently across database backends. These fields are now consistently omitted until the command completes, on all supported databases.

  • The DELETE /api/user/layouts/{id} endpoint of the io.Manager Server REST API now returns status code 404 when no Layout with the given id exists; previously, a nonexistent Layout returned 403.

  • The GET /api/summary endpoint of the io.Manager Server REST API is now available to any authenticated caller and reports only the counts that caller is permitted to see: a dataset is counted when the caller's groups include that dataset's read permission group (e.g. IO_MANAGER:APPS:READ for apps), and is reported as zero otherwise. A caller whose groups include IO_MANAGER:SYSTEM:READ receives the counts for every dataset.

  • Fixed an inconsistency where Layout IDs weren't enforced as unique on MongoDB, while PostgreSQL and Microsoft SQL Server rejected duplicates. Databases created by an earlier version of io.Manager are updated automatically on startup.

  • The POST /users and POST /users/import endpoints of the io.Manager Server REST API now store a password only when the auth_method property is set to "basic". A password supplied under any other authentication method is ignored.

  • Fixed a bug where a failed call to start() left the io.Manager Server's HTTP listener, database connections, and background timers running, so an embedding app couldn't retry the start or shut it down. A failed start now releases them, and an initialization error rejects the returned promise instead of terminating the host process.

  • Improved query performance on Microsoft SQL Server. Queries that filter on a text column, such as the access level of an advanced Layout, now use the available indexes.

  • Fixed a bug where a request to an endpoint that accepts a file upload - such as submitting feedback or a crash report - could fail with status code 500.

  • Fixed a bug where the /user/commands endpoints of the io.Manager Server REST API didn't verify that the requested command or session belongs to the caller. Such requests now return status code 403.

  • Fixed a bug where the POST /user/layouts and POST /v2/user/layouts endpoints of the io.Manager Server REST API, and their bulk counterparts, didn't verify that a Layout named by its ID is one the caller is allowed to save over. Such requests now return status code 404.

  • Fixed a bug where the POST /user/layouts/default and POST /v2/user/layouts/default endpoints of the io.Manager Server REST API didn't verify that the requested Layout is visible to the caller. Such requests now return status code 403.

  • Fixed a bug where the POST /v2/user/query-users and POST /v2/user/query-groups endpoints of the io.Manager Server REST API ran an unbounded search for very short terms. A search term shorter than three characters now returns an empty result set.

  • Added an encoding database column to the blobs table (for PostgreSQL and Microsoft SQL Server only).

⚠️ Note that users who don't use automatic schema migration must create the encoding column and mark the existing rows manually by running the following scripts.

For PostgreSQL:

ALTER TABLE blobs
    ADD COLUMN IF NOT EXISTS encoding VARCHAR(20); -- Encoding of the content: "raw" when it is the file's own bytes, "base64" when it is those bytes base64-encoded as text. Absent on content stored before the encoding was recorded.

-- Only rows that predate the column are NULL, so re-running this script never
-- reclassifies files stored after the upgrade.
UPDATE blobs
    SET encoding = 'base64'
    WHERE type = 'command_result' AND encoding IS NULL;

For Microsoft SQL Server:

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[blobs]')
    AND name = 'encoding'
)
ALTER TABLE blobs
    ADD encoding NVARCHAR(20) NULL; -- Encoding of the content: "raw" when it is the file's own bytes, "base64" when it is those bytes base64-encoded as text. Absent on content stored before the encoding was recorded.
GO

-- Only rows that predate the column are NULL, so re-running this script never
-- reclassifies files stored after the upgrade.
UPDATE blobs
    SET encoding = 'base64'
    WHERE type = 'command_result' AND encoding IS NULL;
  • Fixed a bug where the DELETE /groups/{name} endpoint of the io.Manager Server REST API called a custom Groups service's removeGroup method even when the service reports the canRemoveGroup capability as false, so a service that leaves the method unimplemented failed with status code 500. Such requests now return status code 501.

  • The DELETE /groups endpoint of the io.Manager Server REST API now returns status code 501 when a custom Groups service doesn't report the canRemoveAll capability.

  • The io.Manager Server now calls a custom Groups service's getUserGroups method only when the service reports the canGetUserGroups capability as true. A service that resolves the groups a user belongs to must report that capability; otherwise those groups don't contribute to a user's permissions, and the GET /users/{name}/groups endpoint of the io.Manager Server REST API returns status code 501.

Dependency Changes

The following new dependencies have been added to the package:

  • @node-rs/xxhash version ^1.7.6
  • @scalar/openapi-types version ^0.9.1
  • archiver version ^8.0.0

The following dependencies have been removed from the package:

  • lodash

The following dependencies have been updated:

  • @interopio/schemas from version ^9.7.0 to ^10.0.0
  • @nestjs/swagger from version 11.2.3 to ^11.2.3
  • tedious from version ^19.1.3 to ^20.0.0

2.0

2.1.1

Release date: 22.05.2026

Improvements & Bug Fixes

  • Fixed a bug where the traceContext field of each io.Manager command was returned as a JSON-encoded string instead of an object when using Microsoft SQL Server as a database, which caused the io.Connect platform to fail response validation and be unable to open a session when OpenTelemetry tracing is enabled.

2.1.0

Release date: 30.01.2026

Breaking Changes

  • Dropped support for PostgreSQL 13. io.Manager now requires PostgreSQL 14 or later.

New Features

  • Added support for Node.js 24 LTS. io.Manager now requires Node.js 20 LTS, 22 LTS, or 24 LTS.

  • Added the following properties to the auth_basic top-level key of the optional Config object for initializing the io.Manager Server:

Property Type Description
sessionLifetime number Interval in seconds at which the session will expire and the user will be logged out of the io.Manager Admin UI. Valid only if useSessionCookie is set to true and the CORS options are configured properly via the cors property. Defaults to 3600.
useSessionCookie boolean If true (default), io.Manager will use a signed JWT stored in a session cookie to manage the Admin UI user sessions. Set to false to disable session cookies. If session cookies are enabled, it's required to specify CORS options via the cors property.

The following example demonstrates how to increase the Admin UI user session to two hours when using Basic authentication:

import { start } from "@interopio/manager";

const config = {
    // Enabling Basic authentication.
    auth_method: "basic",
    // Additional settings for Basic authentication.
    auth_basic: {
        predefinedUsers: ["admin:admin"],
        // Configuring the user session length for the Admin UI.
        sessionLifetime: 7200
    },
    // It's required to specify CORS options when session cookies are enabled.
    cors: {
        credentials: true,
        origin: "http://localhost:3000"
    }
};

const server = await start(config);

You can also use the following environment variables:

Environment Variable Description
API_AUTH_METHOD_BASIC_SESSION_LIFETIME Interval in seconds at which the session will expire and the user will be logged out of the io.Manager Admin UI. Valid only if API_AUTH_METHOD_BASIC_USE_SESSION_COOKIE is set to true and the CORS options are configured properly via the API_CORS_OPTIONS environment variable. Defaults to 3600.
API_AUTH_METHOD_BASIC_USE_SESSION_COOKIE If true (default), io.Manager will use a signed JWT stored in a session cookie to manage the Admin UI user sessions. Set to false to disable session cookies. If session cookies are enabled, it's required to specify CORS options via the API_CORS_OPTIONS environment variable.

The following example demonstrates how to increase the Admin UI user session to two hours when using Basic authentication:

API_AUTH_METHOD=basic
API_AUTH_METHOD_BASIC_USERS=["admin:admin"]

# Configuring the user session length for the Admin UI.
API_AUTH_METHOD_BASIC_SESSION_LIFETIME=7200
# It's required to specify CORS options when session cookies are enabled.
API_CORS_OPTIONS={"credentials": true, "origin": "http://localhost:3000"}
  • Added a sessions top-level key to the optional Config object for initializing the io.Manager Server. Use this property to provide configuration for the io.Connect platform sessions. The sessions object has the following properties:
Property Type Description
inactiveSessionTimeoutInSeconds number Interval in seconds after which an io.Connect platform client session is considered inactive. Sessions are considered active if the connected platform has fetched data within the specified timeout. Must be set to the same value as the "fetchInterval" property of the "server" object in the io.Connect Desktop platform configuration, or the fetchInterval property of the manager object in the io.Connect Browser platform configuration respectively. Defaults to 30.

The following example demonstrates how to configure the inactive session timeout:

import { start } from "@interopio/manager";

const config = {
    // Configuration for the io.Connect platform sessions.
    sessions: {
        // You must specify the same value for the `fetchInterval` property
        // in the platform configuration for connecting to io.Manager.
        inactiveSessionTimeoutInSeconds: 90
    }
};

const server = await start(config);

You can also use the following environment variable:

Environment Variable Description
API_SESSIONS_INACTIVE_SESSION_TIMEOUT Interval in seconds after which an io.Connect platform client session is considered inactive. Sessions are considered active if the connected platform has fetched data within the specified timeout. Must be set to the same value as the "fetchInterval" property of the "server" object in the io.Connect Desktop platform configuration, or the fetchInterval property of the manager object in the io.Connect Browser platform configuration respectively. Defaults to 30.
  • New REST API endpoints have been added:
New Endpoint Description
GET /commands/descriptions/all Retrieves all supported commands across all io.Connect platform versions. The response is in the form of an array of JSON objects. Each object of the resulting array describes a unique supported command and contains information about the command type, the command description, version-specific command parameter examples, and a list of the io.Connect platform versions which support the command.

Improvements & Bug Fixes

  • Fixed inconsistent handling of the accessList property of an app across stores when set to null.
  • Fixed a bug where database schema names weren't properly quoted in PostgreSQL making it impossible to use PostgreSQL keywords as schema names.
  • Fixed a bug where updates to the machine table were failing on Microsoft SQL Server.
  • Fixed a bug where io.Manager failed to merge configurations when identifier.version was missing the minor or patch segments.

Dependency Changes

The following dependencies have been updated:

  • @nestjs/swagger from version ^11.2.0 to ^11.2.3
  • dotenv from version 17.0.0 to ^17.2.1
  • mime from version ^1.6.0 to ^4.1.0
  • mongodb from version ^6.16.0 to ^7.0.0
  • mssql from version ^11.0.1 to ^12.2.0
  • tedious from version ^18.6.1 to ^19.1.3

2.0.0

Release date: 11.07.2025

⚠️ Note that the following io.Manager NPM packages will now be published in the public NPM registry:

Previous versions of the packages will still be available in the private JFROG registry.

Breaking Changes

ℹ️ For more details on the breaking changes and the required migration steps, see also the Upgrade section.

⚠️ Note that as of version 2.0.0, io.Manager requires a license key to operate. To acquire a license key, contact us at sales@interop.io. Existing customers should contact their Client Success representative to obtain a license key.

ℹ️ For more details on licensing, see the Requirements > Licensing section.

  • Added a required licenseKey top-level property in the configuration object for initializing the io.Manager Server. Use this property to supply your io.Manager license key:
import { start } from "@interopio/manager";

const config = {
    // It's now required to provide a valid license key for io.Manager to operate.
    licenseKey: "my-license-key"
};

const server = await start(config);

You can also use the API_LICENSE_KEY environment variable:

API_LICENSE_KEY=my-license-key
  • Dropped Node.js 18 support. io.Manager now requires Node.js 20 LTS or 22 LTS.
  • The default value for skipProcessExitOnStop is changed to true. If you want process.exit() to be called when server.stop() is called or when the server fails to start, set skipProcessExitOnStop to false.
  • Removed the store property of the Server interface.
  • Removed the UserStore, Store, and BLOBStore interfaces.
  • TypeScript types:
    • The AuditService.getAll() method now returns AuditLogDataResult instead of DataResult<AuditLog>.
    • When implementing CustomAuthenticator, you now have to import the following types from @interopio/manager instead of @interopio/manager-api:
      • Token
      • User
    • When implementing AuditService, you now have to import the following types from @interopio/manager instead of @interopio/manager-api:
      • AuditLog
      • AuditLogDataResult
      • CleanAuditLogRequest
      • DataRequest
      • User
    • When implementing GroupsService, you now have to import the following types from @interopio/manager instead of @interopio/manager-api:
      • DataRequest
      • Group
      • GroupDataResult
      • GroupsFeatures
      • User
    • The types AuditLogEntityType and AuditLogOperation have been converted from TypeScript Union types to TypeScript Enums.
  • Removed the previously deprecated status_endpoint top-level configuration property.
  • The GET /db endpoint now always returns a 500 status code regardless of the configured database. Previously, this only worked for MongoDB and returned database statistics specific to MongoDB.
  • Removed false, "none", and "sentry" as values for the monitoring property. Use { type: "none" } or { type: "sentry" } instead.
  • Removed the tracesSampleRate property from the Sentry monitoring configuration. Use sentryOptions.tracesSampleRate instead.
  • Removed the dsn property from the Sentry monitoring configuration. Use sentryOptions.dsn instead.
  • Tracing via the Sentry SDK is no longer supported. The recommended way to send traces to Sentry is by using OpenTelemetry traces. See also the OpenTelemetry with Sentry example on GitHub.
  • Removed ["admin"] as a default value for the API_AUTH_EXCLUSIVE_USERS environment variable. This change doesn't affect users who don't use environment variables for configuring io.Manager as the default value was valid only for the API_AUTH_EXCLUSIVE_USERS environment variable.
  • Due to a change in the FDC3 specification, the name and definition.name app properties won't be checked for consistency for FDC3 app definitions. The name and definition.appId properties will still be checked. For more details, see the FDC3 Standard 2.1.
  • When using environment variable configuration with .env files, values from the .env files will override the process environment variables. This means that if an environment variable is both defined in the .env file and passed to the process, the value from the .env file will be used.

New Features

⚠️ Note that users who don't use automatic schema migration must create the traceContext column manually by running the following scripts.

For PostgreSQL:

ALTER TABLE commands
    ADD COLUMN IF NOT EXISTS "traceContext" JSON;

For Microsoft SQL Server:

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[commands]')
    AND name = 'traceContext'
)
ALTER TABLE commands
    ADD traceContext NVARCHAR(MAX);
  • Changed the type of the commandParams database column of the commands table to JSON (for PostgreSQL only).

⚠️ Note that users who don't use automatic schema migration must change the type of the commandParams column manually by running the following script:

ALTER TABLE commands
    ALTER COLUMN "commandParams" TYPE JSON USING "commandParams"::json;
  • Changed the precision of the weight database column in the glue42SystemConfig table (for PostgreSQL and Microsoft SQL Server only).

⚠️ Note that users who don't use automatic schema migration must change the precision of the weight column manually by running the following scripts.

For PostgreSQL:

ALTER TABLE "glue42SystemConfig"
   ALTER COLUMN weight TYPE numeric(8, 6) USING weight::numeric(8, 6);

For Microsoft SQL Server:

ALTER TABLE glue42SystemConfig
    ALTER COLUMN weight decimal(8, 6);
  • Changed the type of the definition database column of the layouts table to JSON (for PostgreSQL and Microsoft SQL Server only).

⚠️ Note that users who don't use automatic schema migration must change the type of the definition column manually by running the following scripts.

For PostgreSQL:

ALTER TABLE layouts
    ALTER COLUMN definition TYPE JSON USING definition::json;

For Microsoft SQL Server:

ALTER TABLE layouts
    ALTER COLUMN definition NVARCHAR(max) NOT NULL;
  • Changed the type of the comment database column of the crashes table to NVARCHAR(max) (for Microsoft SQL Server only).

⚠️ Note that users who don't use automatic schema migration must change the type of the comment column manually by running the following script:

ALTER TABLE crashes
    ALTER COLUMN comment NVARCHAR(max);
  • Changed the types of the comment, description and attachment database columns of the feedback table to NVARCHAR(max) (for Microsoft SQL Server only).

⚠️ Note that users who don't use automatic schema migration must change the type of the columns manually by running the following script:

ALTER TABLE feedback
    ALTER COLUMN comment NVARCHAR(max);
ALTER TABLE feedback
    ALTER COLUMN description NVARCHAR(max) NOT NULL;
ALTER TABLE feedback
    ALTER COLUMN attachment NVARCHAR(max) NOT NULL;
  • Added an auth_timeout top-level key in the configuration object for initializing the io.Manager Server, which accepts a number as a value and defaults to 120000. Use this property to specify an interval in milliseconds to wait for the authentication process request to complete before rejecting it. This setting is useful when you have implemented a custom authentication mechanism:
import { start } from "@interopio/manager";
import { MyAuthenticator } from "./MyAuthenticator";

const config = {
    auth_method: "custom",
    auth_custom: new MyAuthenticator(),
    auth_timeout: 60000
};

const server = await start(config);

You can also use the API_AUTH_TIMEOUT environment variable:

API_AUTH_TIMEOUT=60000
  • Added a cors top-level key in the configuration object for initializing the io.Manager Server. The value will be passed to the cors middleware. For more details, see the Configuration Options section in the cors package documentation.

  • Added an interceptProcessSignals top-level key in the configuration object for initializing the io.Manager Server. If set to true, io.Manager will listen for the following signals: "SIGINT", "SIGTERM", "SIGHUP", "SIGBREAK", "SIGQUIT". Defaults to false. You can also use the API_INTERCEPT_PROCESS_SIGNALS environment variable.

  • Added new properties to the purge top-level key in the configuration object for initializing the io.Manager Server. Use these properties to configure the data purging functionality of io.Manager:

Property Type Description
purgeCommandsAfterDays number Number of days after which an executed command and the respective command result become eligible for purging. Set to -1 to disable purging of commands and command results. Defaults to 90.
scheduledTaskInterval number Interval in milliseconds at which to run the periodic data purging operation. Defaults to 86400000 (1 day).

You can also use the following environment variables:

Environment Variable Description
API_PURGE_COMMANDS_AFTER_DAYS Number of days after which an executed command and the respective command result become eligible for purging. Set to -1 to disable purging of commands and command results. Defaults to 90.
API_PURGE_SCHEDULED_TASK_INTERVAL Interval in milliseconds at which to run the periodic data purging operation. Defaults to 86400000 (1 day).
  • Added a sentryClient property to the SentryMonitoringConfig object. Use to provide an already initialized Sentry client. Useful if you want to initialize Sentry manually before starting io.Manager. For more details, see the Sentry for Node.js guide in the official Sentry documentation.

  • Added a sentryOptions property to SentryMonitoringConfig object. Passed to the Sentry.init() method. Ignored when a Sentry client is provided via the sentryClient property. For more details, see the Configuration Options section in the official Sentry documentation.

  • Added a customPropagator property to OtelTracingConfig object. Use to provide a custom OpenTelemetry propagator.

  • Added a customContextManager property to OtelTracingConfig object. Use to provide a custom OpenTelemetry context manager.

  • Added a new API_LOG_LEVEL environment variable. Use to set the log4js log level when using environment variable configuration. Defaults to info.

Improvements & Bug Fixes

  • Added a new CustomAuthUnauthorizedError export. Used in custom authenticator implementations.
  • Added a new OtelConfig export. Exports the existing OpenTelemetry configuration type.
  • The name top-level configuration property is now optional. If not provided, the server will use the default value of "local".
  • The port top-level configuration property is now optional. If not provided, the server will use the default value of 4356.

Deprecated Endpoints

The following REST API endpoints have been deprecated:

Deprecated Endpoint New Endpoint
GET /db Use GET /db-connectivity instead.
GET /server/info Use GET /v2/server/info instead.

Dependency Changes

The following new dependencies have been added to the package:

  • @nestjs/common version ^11.1.0
  • @nestjs/core version ^11.1.0
  • @nestjs/platform-express version ^11.1.0
  • @nestjs/swagger version ^11.2.0
  • @opentelemetry/instrumentation-nestjs-core version ^0.46.0
  • mime version ^1.6.0
  • rxjs version ^7.8.2
  • tedious version ^18.6.1

The following dependencies have been removed from the package:

  • @interopio/manager-api
  • @sentry/tracing
  • @types/bcryptjs
  • @types/cookie-parser
  • @types/lodash
  • @types/multer
  • @types/shortid
  • ajv
  • cross-env

The following dependencies were updated:

  • @interopio/otel from version ^0.0.14 to ^0.0.67
  • @interopio/schemas from version ^9.2.0 to ^9.7.0
  • @okta/jwt-verifier from version ^3.1.0 to ^4.0.1
  • @opentelemetry/api-logs from version ^0.54.0 to ^0.200.0
  • @opentelemetry/exporter-logs-otlp-http from version ^0.54.0 to ^0.200.0
  • @opentelemetry/exporter-metrics-otlp-http from version ^0.54.0 to ^0.200.0
  • @opentelemetry/exporter-trace-otlp-http from version ^0.54.0 to ^0.200.0
  • @opentelemetry/instrumentation from version ^0.54.0 to ^0.200.0
  • @opentelemetry/instrumentation-express from version ^0.43.0 to ^0.49.0
  • @opentelemetry/instrumentation-http from version ^0.54.0 to ^0.200.0
  • @opentelemetry/instrumentation-knex from version ^0.40.0 to ^0.45.0
  • @opentelemetry/instrumentation-mongodb from version ^0.47.0 to ^0.53.0
  • @opentelemetry/instrumentation-undici from version ^0.6.0 to ^0.11.0
  • @opentelemetry/resources from version ^1.27.0 to ^2.0.0
  • @opentelemetry/sdk-logs from version ^0.54.0 to ^0.200.0
  • @opentelemetry/sdk-metrics from version ^1.27.0 to ^2.0.0
  • @opentelemetry/sdk-trace-base from version ^1.27.0 to ^2.0.0
  • @opentelemetry/sdk-trace-node from version ^1.27.0 to ^2.0.0
  • @opentelemetry/semantic-conventions from version ^1.27.0 to ^1.33.0
  • @sentry/node from version ^7.13.0 to ^9.18.0
  • bcryptjs from version ^2.4.3 to ^3.0.2
  • cookie-parser from version ^1.4.6 to ^1.4.7
  • dotenv from version ^16.4.5 to 17.0.0
  • express from version ^4.17.1 to ^5.1.0
  • express-oauth2-jwt-bearer from version ^1.4.1 to ^1.6.1
  • jsonwebtoken from version ^9.0.0 to ^9.0.2
  • knex from version ^2.4.1 to ^3.1.0
  • mongodb from version ^4.11.0 to ^6.16.0
  • mssql from version ^9.1.1 to ^11.0.1
  • multer from version npm:@interopio/multer@^1.4.5-lts.1 to npm:@interopio/multer@^2.0.0
  • pg from version ^8.7.3 to ^8.16.0
  • reflect-metadata from version ^0.1.13 to ^0.2.2
  • semver from version ^7.5.2 to ^7.7.2
  • shortid from version ^2.2.8 to ^2.2.17

1.0

1.8.2

Release date: 27.03.2025

Improvements & Bug Fixes

  • Removed the "user" field from the serverx-token header passed from io.Connect to io.Manager.

1.8.1

Release date: 26.02.2025

New Features

  • It's now possible to define custom status messages for health checks:
    • Added a healthCheckStatus optional string property to the HealthEndpointsConfig object. The string will be passed as a value to the "status" field in the health check response. You can also use the API_HEALTH_ENDPOINTS_CUSTOM_HEALTHCHECK_STATUS environment variable.
    • Added a databaseHealthCheckStatus optional string property to the HealthEndpointsConfig object. The string that the database connectivity health check will return as a successful response. You can also use the API_HEALTH_ENDPOINTS_CUSTOM_DB_CONNECTIVITY_STATUS environment variable.

1.8.0

Release date: 24.02.2025

New Features

  • Implemented additional health check endpoints on custom routes:
    • Added a customHealthCheckRoute optional string property to the HealthEndpointsConfig object. If present, an additional health check endpoint will be available on the specified route. You can also use the API_HEALTH_ENDPOINTS_CUSTOM_HEALTHCHECK_ROUTE environment variable.
    • Added a customDatabaseHealthCheckRoute optional string property to the HealthEndpointsConfig object. If present, an additional database health check endpoint will be available on the specified route. You can also use the API_HEALTH_ENDPOINTS_CUSTOM_DB_CONNECTIVITY_HEALTHCHECK_ROUTE environment variable.

1.7.4

Release date: 20.02.2025

Improvements & Bug Fixes

  • Relaxed the DataRequest JSON filter validation to safely allow using arbitrary JSON fields.

1.7.3

Release date: 20.11.2024

Improvements & Bug Fixes

  • Fixed a bug where sometimes the io.Manager Server would fail to load environment variables from a file.

1.7.2

Release date: 19.11.2024

Improvements & Bug Fixes

  • Added consistency validation for apps:
    • name and definition.name must be the same;
    • name and definition.appId (if present) must be the same;
  • Added consistency validation for Layouts:
    • name and definition.name must be the same;
    • type and definition.type must be the same;

1.7.1

Release date: 07.11.2024

New Features

  • Exposed appsV2 and layoutsV2 via the Server interface for custom scripts.

Improvements & Bug Fixes

  • Fixed a bug where some PostgreSQL queries wouldn't use the correct schema.

1.7.0

Release date: 01.11.2024

New Features

  • Added OpenTelemetry support.
  • Added new environment variable API_MONITORING_SENTRY_DSN - custom Sentry DSN to use when using monitoring via Sentry.
  • Added new environment variable API_AUTH_AUTH0_ISSUER_BASE_URL for Auth0 authentication - base URL for the Auth0 issuer.
  • Added database connectivity health check as well as Docker health checks for the io.Manager Server and Admin UI Docker images.
  • Added a purgeAtStartupEnabled optional Boolean property to the PurgeConfig object. If set to true (default), will run the purge task at startup of the io.Manager Server. You can also use the API_PURGE_AT_STARTUP_ENABLED environment variable to configure the purge behavior.
  • Added database column others to the last_updated table (for PostgreSQL and Microsoft SQL Server only).

⚠️ Note that users who don't use automatic schema migration must create the others column manually by running the following scripts.

For PostgreSQL:

ALTER TABLE last_updated
    ADD COLUMN IF NOT EXISTS others JSON NULL;

For Microsoft SQL Server:

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[last_updated]')
      AND name = 'others'
)
ALTER TABLE last_updated
    ADD others NVARCHAR(MAX);

1.6.4

Release date: 11.09.2024

Improvements & Bug Fixes

  • Fixed a bug where io.Manager would fail to save a crash dump generated by io.Connect Desktop.

1.6.3

Release date: 10.09.2024

Improvements & Bug Fixes

  • Fixed a bug where invoking controllers directly via the RestServer class would throw an error.

1.6.2

Release date: 05.09.2024

Improvements & Bug Fixes

  • Moved dotenv package from "devDependencies" to "dependencies" to fix a bug introduced in v1.6.0.

1.6.1

Release date: 04.09.2024

Improvements & Bug Fixes

  • Fixed a bug where the total record count wasn't calculated correctly for data requests to PostgreSQL and Microsoft SQL Server.

1.6.0

Release date: 29.08.2024

New Features

  • Implemented automatic failover when using PostgreSQL databases with io.Manager. To specify multiple PostgreSQL hosts, use the hosts property of the store object for configuring the connection to a PostgreSQL database. The hosts property accepts an array of objects describing PostgreSQL database hosts that will be tried in the order they are provided.

The following example demonstrates configuring io.Manager Server to use multiple PostgreSQL hosts:

import { start } from "@interopio/manager";

// Configuration for the io.Manager Server.
const config = {
    name: "my-server",
    port: 4242,
    token: {
        secret: "my-secret"
    },
    // Configuration for connecting to a PostgreSQL database.
    store: {
        type: "postgresql",
        // Specifying multiple PostgreSQL hosts to which to connect.
        hosts: [
            {
                connection: "postgresql://my_user:password@localhost:5432",
                dbName: "my_db",
                schemaName: "my_schema"
            },
            {
                connection: "postgresql://my_user:password@localhost:5433",
                dbName: "my_db",
                schemaName: "my_schema",
                // io.Manager won't attempt to execute write operations on this host.
                isReadOnly: true
            }
        ]
    }
};

// Initializing the io.Manager Server.
const server = await start(config);

Improvements & Bug Fixes

  • Added database column browser to the machines table (for PostgreSQL and Microsoft SQL Server only).

⚠️ Note that users who don't use automatic schema migration must create the browser column manually by running the following scripts.

For PostgreSQL:

ALTER TABLE machines
    ADD COLUMN IF NOT EXISTS browser JSON NULL;

For Microsoft SQL Server:

IF NOT EXISTS (
    SELECT *
    FROM   sys.columns
    WHERE  object_id = OBJECT_ID(N'[dbo].[machines]')
    AND name = 'browser'
)
ALTER TABLE machines
    ADD browser NVARCHAR(MAX);
  • Fixed a bug that would prevent MongoDB users from seeing feedback in the feedback list in the io.Manager Admin UI.
  • Layouts can now be filtered by the id field using the Admin API.

1.5.0

Release date: 16.08.2024

New Features

  • New REST API endpoints have been added:
New Endpoint Description
GET /v2/server/info Returns information about the io.Manager Server, such as server version and available capabilities.
POST /v2/apps/add Creates a new app definition or returns status code 400 if an app definition with the same name property already exists. Returns the created app definition.
POST /v2/apps/add-or-update Creates a new app definition or updates an existing one if an app definition with the same name property already exists. Returns the created or updated app definition.
POST /v2/apps/update Updates an app definition or returns status code 404 if an app definition with the specified name property isn't found. Returns the updated app definition.
POST /v2/layouts/add Creates a new Layout definition or returns status code 400 if a Layout definition with the same combination of name, type and owner properties already exists. Returns the created Layout definition.
POST /v2/layouts/add-or-update Creates a new Layout definition or updates an existing one if a Layout definition with the same id property (or the same combination of name, type and owner properties if id isn't provided) already exists. Returns the created or updated Layout definition.
POST /v2/layouts/update Updates a Layout definition or returns status code 404 if a Layout definition with the specified id property (or the specified combination of name, type and owner properties if id isn't provided) isn't found. Returns the updated Layout definition.

Deprecated Endpoints

The following REST API endpoints have been deprecated:

Deprecated Endpoint New Endpoint
POST /apps Use POST /v2/apps/add-or-update instead.
POST /apps/:name Use POST /v2/apps/update instead.
POST /layouts Use POST /v2/layouts/add-or-update instead.
POST /users/:name/layouts Use POST /v2/layouts/add instead.

1.4.0

Release date: 15.05.2024

New Features

  • Support for Okta authentication.
  • Implemented "empty" DataRequest filter type for all filter types where applicable.

Improvements & Bug Fixes

  • Added inRange filters support for MongoDB.
  • Fixed preferences upsert.

1.3.0

Release date: 26.04.2024

New Features

  • DB purge operation now happens at startup.
  • Exposed extra Microsoft SQL Server options.

Improvements & Bug Fixes

  • Fixed inconsistent group resolution between apps and Layouts in io.Manager.
  • Fixed broken last updated timestamps in io.Manager leading to unnecessary database requests and hashing operations when configured with PostgreSQL or Microsoft SQL Server.
  • Fixed summary endpoint on Microsoft SQL Server and PostgreSQL.
  • DB purge interval is stopped when server.stop() is called.
  • Remove server-x token groups usage in sessions service.

1.2.0

Release date: 22.03.2024

New Features

  • Added a configurable purge job for purging old audit logs, feedback and crashes in order to reduce database usage.
  • Added API_STORE_POSTGRESQL_NATIVE_PG_DRIVER and API_STORE_MSSQL_DOMAIN environment variables.

Improvements & Bug Fixes

  • Implemented ORFilters and excludeFields for PostgreSQL and Microsoft SQL Server.
  • User groups defined in database are considered when using custom auth provider and the data store.
  • Rebuilt the Data Request functionality for PostgreSQL and Microsoft SQL Server to use SQL parameters. Fixed various related bugs.
  • Added migration for missing constraints in layouts and prefs tables in PostgreSQL and Microsoft SQL Server.

1.1.1

Release date: 06.03.2024

Improvements & Bug Fixes

  • When a user saves a shared Layout, a new private copy of that Layout is created. Previously, the shared Layout was transferred to the user that had saved it.

1.1.0

Release date: 24.02.2024

New Features

⚠️ Note that pg-native isn't a dependency of the @interopio/manager package and you must install it separately in your project.

Improvements & Bug Fixes

  • Microsoft SQL Server support: fixed a bug where the accessList column of the apps table wasn't written if empty.
  • Updated dependencies.

1.0.0

Release date: 17.01.2024

Breaking Changes

  • Users can now be compared in a case-insensitive way. Added new a configuration property username_case_sensitive and a new environment variable API_USERNAME_CASE_SENSITIVE that can be used to switch between case-sensitive and case-insensitive modes. Defaults to case-insensitive.
  • Removed status endpoint, because the express-status-monitor package is deprecated.

Improvements & Bug Fixes

  • Updated dependencies.

0.1

0.20.1

Release date: 20.10.2023

Improvements & Bug Fixes

  • Updated dependencies.

0.20.0

Release date: 09.10.2023

New Features

  • Added option for controlling audit logs.

Improvements & Bug Fixes

  • Stopped recreating controllers on every request.
  • Disabled auditing for opening sessions open and creating users.

0.19.2

Release date: 05.09.2023

Improvements & Bug Fixes

  • Fixed updating a Layout without a Layout ID.

0.19.1

Release date: 14.07.2023

Improvements & Bug Fixes

  • Moved several types from development dependencies to dependencies.

0.19.0

Release date: 14.07.2023

New Features

0.18.0

Release date: 15.06.2023

New Features

  • Added explicit Layouts support for PostgreSQL.
  • Auth0 authentication now uses permissions.

Improvements & Bug Fixes

  • Fixed rejecting hello request from io.Connect Browser.

0.17.1

Release date: 30.05.2023

Improvements & Bug Fixes

  • Moved some of the libraries into the codebase for better control over the dependencies.

0.17.0

Release date: 22.05.2023

New Features

  • Exposed APIs (e.g., for users, apps, Layouts and more) attached to the initialized server object.

0.16.0

Release date: 19.05.2023

New Features

  • Added ability to set explicit Layouts for users.

0.15.2

Release date: 18.05.2023

Improvements & Bug Fixes

  • Optimized fetching of user Layouts.

0.15.1

Release date: 11.05.2023

Improvements & Bug Fixes

  • Exposed all options for Auth0 configuration.

0.15.0

Release date: 11.05.2023

New Features

  • Restored Auth0 authentication support.

0.14.0

Release date: 11.04.2023

New Features

  • Added an extra pool setting for PostgreSQL connections.

0.13.0

Release date: 31.03.2023

Improvements & Bug Fixes

  • Updated to latest @interopio/schemas.

0.12.1

Release date: 16.03.2023

Improvements & Bug Fixes

  • Updated dependencies.

0.12.0

Release date: 26.01.2023

New Features

  • Allow a single default Layout entry in the database.

Improvements & Bug Fixes

  • Prevented users from overwriting common Layouts.

0.11.0

Release date: 25.11.2022

New Features

  • Added HTTPS support.

0.10.0

Release date: 28.10.2022

New Features

0.9.11

Release date: 12.10.2022

Improvements & Bug Fixes

0.9.10

Release date: 10.10.2022

Improvements & Bug Fixes

  • Fixed always converting the port in the server configuration to a number. Now, if a string is passed, it's treated as a path that might be a named pipe.

0.9.9

Release date: 21.09.2022

New Features

0.9.8

Release date: 19.09.2022

Improvements & Bug Fixes

0.9.7

Release date: 19.09.2022

Improvements & Bug Fixes

  • Removed sort memory limit.

0.9.6

Release date: 19.09.2022

Improvements & Bug Fixes

  • Improved the shutdown process.

0.9.5

Release date: 07.09.2022

Improvements & Bug Fixes

  • Improved Sentry monitoring routes.

0.9.4

Release date: 24.08.2022

New Features

  • Added Sentry monitoring.

0.8.4

Release date: 07.06.2022

Improvements & Bug Fixes

  • Updated to latest @interopio/server-api.

0.8.3

Release date: 07.06.2022

Improvements & Bug Fixes

  • Hashed passwords (only used in Basic authentication).
  • Updated dependencies.

0.8.2

Release date: 23.03.2022

Improvements & Bug Fixes

  • Handled schema validator errors.

0.8.1

Release date: 23.03.2022

Improvements & Bug Fixes

  • Fixed schema path.

0.8.0

Release date: 22.03.2022

New Features

  • Added a /schema endpoint.

0.7.0

Release date: 21.03.2022

New Features

  • Added a flow for Basic authentication.
  • Added a /whoami endpoint.

0.6.0

Release date: 17.03.2022

New Features

  • Added a default route.
  • Added a stop route that is registered only if an environment variable is present in the configuration.

0.5.0

Release date: 13.03.2022

New Features

  • Added a /capabilities endpoint for exposing the features of Groups.

0.4.1

Release date: 13.03.2022

Improvements & Bug Fixes

  • Included the tests as pre-publish step.

0.4.0

Release date: 13.03.2022

New Features

  • Added the option to provide a custom Groups service.