Skip to content

Browser Bridge API ​

Hosted extensions use window.isaExtensionBridge inside the extension iframe. The host injects workspace authentication; extensions must not handle dashboard tokens directly.

Host mods are not bridge APIs ​

The browser bridge never exposes parent.document, evaluates extension strings, or activates host modules. A normal extension, including one that sends forged postMessage traffic or happens to contain an undeclared JavaScript file, cannot style the parent application or add host UI.

Only an installed extension with the exact mod permission and a validated modEntry can cross that boundary. After an interactive host warning, the separately loaded classic script registers through window.__ISA_WARDEN_EXTENSION_MOD_HOST__ in the host realm. That privileged registration and lifecycle context are documented in Manifest specification: privileged host modules; they are deliberately unavailable inside the iframe.

Keep the iframe app functional when mod activation is declined or unavailable. All ordinary data access, host commands, and extension UI continue to use window.isaExtensionBridge.

Core Methods ​

invoke(command, args) ​

Calls a host command directly.

js
await window.isaExtensionBridge.invoke('get_app_info', {});

invokeNative(functionName, args) ​

Calls a function exposed by the extension's own native binary.

on(eventName, callback) And off(eventName, callback) ​

Subscribe and unsubscribe from host bridge events.

getAppInfo() ​

Convenience wrapper for get_app_info.

addLog(message, metadata) ​

Writes a host-visible extension log entry.

streamChat(options, handlers) ​

Invokes host chat streaming with the active extension thread context.

Filesystem Model ​

Dashboard files can be visible to a user through workspace permissions and group links. That visibility does not automatically grant an extension access. An extension gets filesystem access only through:

  • ensureGroupFolder() for extension-owned group storage.
  • openWorkspaceFile() for a user-selected file grant.
  • openWorkspaceFolder() for a user-selected folder grant.

Local grants are scoped by extension id, workspace id, and launch group. Users can revoke them in Settings. Read-only grants allow reads and listings but block uploads, replacements, and deletes.

Filesystem Helpers ​

ensureGroupFolder() ​

Creates or finds the extension's folder for the selected launch group and returns its real workspace file id and path.

js
const folder = await window.isaExtensionBridge.ensureGroupFolder();
console.log(folder.path);

The folder is created only when requested. Extensions should place shared syncable files below the returned path. Access to this folder is derived from the extension being assigned to the selected launch group.

openWorkspaceFile(args) ​

Opens the host workspace-file picker and stores a local file grant if the user confirms the selection.

js
const selected = await window.isaExtensionBridge.openWorkspaceFile({
  access: 'read'
});

Use access: 'read' for read-only grants or access: 'read_write' when the extension needs to update the selected file.

openWorkspaceFolder(args) ​

Opens the host workspace-folder picker and stores a local folder grant.

js
const selected = await window.isaExtensionBridge.openWorkspaceFolder({
  access: 'read_write'
});

listDir(args) ​

Lists files below a granted folder or the extension group folder, merging dashboard inventory with locally available syncable entries.

js
const listing = await window.isaExtensionBridge.listDir({
  path: folder.path,
  recursive: false
});

The response uses files, not entries, and reports whether it is authoritative. Use exactly one of path or pathPrefix; recursive defaults to true.

getFiles(args) ​

Reads one or more granted files by canonical path. Returned file IDs are metadata only and are never read selectors. The host always checks its clean local sync cache first for syncable files, then retrieves only unresolved files from the dashboard.

js
const response = await window.isaExtensionBridge.getFiles({
  files: [{ path: selected.path }]
});
const file = response.items[0];

The response has an items array, with one result per requested path. Each item includes text or base64 bytes depending on content. Supply cachePolicy: "dashboard-required" per file only when a canonical dashboard read is needed, such as before CAS writes or when establishing authoritative absence.

uploadFiles(args) ​

Uploads or replaces one or more files below a read-write grant. A single upload is represented by a one-item files list.

js
await window.isaExtensionBridge.uploadFiles({
  files: [{
    path: `${folder.path}/state.json`,
    text: JSON.stringify({ ok: true }),
    contentType: 'application/json',
    metadata: { syncable: 'true' },
    progressId: 'state-upload'
  }]
});

Progress is emitted as filesystem-upload-progress, scoped by the host to the frozen workspace and group, and should be filtered by progressId when concurrent uploads are possible.

deleteFile(args) ​

Deletes a granted file or folder. Read-only grants reject delete. Folder trees may be deleted with recursive: true; conditional deletion accepts expectedSyncRevision and expectedContentHash.

discardPendingFile(args) ​

Discards only a conflicted local pending version of a syncable file and restores the last dashboard-confirmed cache version. It cannot discard an ordinary dirty or synced file.

getDownloadLinkFile(args) ​

Creates a signed URL for a granted file. This remains available for explicit download workflows, but syncable content should be read with getFiles so a complete local copy can be returned without an S3 round trip.

Syncable Files ​

Extensions may mark small group-folder files as syncable with metadata.syncable = 'true'. The host handles local cache, offline queueing, group-wide download, and conflict handling. Extensions should not build their own offline sync for syncable files.

The opt-in is the exact string 'true'. The group folder and read-write grant must first be provisioned while online. After that, uploadFiles durably saves syncable text, bytesBase64, or snapshotted filePath content locally before contacting the dashboard. A successful pending/local result means the content is readable offline and survives restart; remote acceptance is confirmed later by host reconciliation. New filenames below the provisioned folder follow the same rule. Conflicts retain the local bytes, while invalid paths, missing login, revoked/read-only grants, and group mismatches do not create queued content.

Reading that content requires only getFiles; the extension never needs to request a dashboard download link for a syncable file.

Uploads without this exact metadata value keep the online-only behavior.

Filesystem responses expose host sync metadata where available, including syncStatus, syncRevision, contentHash, baseContentHash, pendingUpload, pendingState, conflictCount, and lastSyncedAt.

Shared Project Helpers ​

Shared projects provide one stable, group-scoped identity that multiple extensions can use. Discover the API before showing a project picker:

js
const bridge = window.isaExtensionBridge;
const appInfo = await bridge.getAppInfo();

if (appInfo.projects?.canRead) {
  const { projects } = await bridge.listProjects();
  console.log(projects);
}

listProjects() invokes projects_list. createProject({ name }) invokes projects_create and requires projects_write. The default list contains only active projects. Pass includeArchived: true when an archive or restore view also needs archived project metadata:

js
const { projects: allProjects } = await bridge.listProjects({
  includeArchived: true
});

const result = await bridge.createProject({ name: 'Renovatie West' });
console.log(result.project.id, result.created);

Project objects contain id, name, RFC 3339 createdAt and updatedAt strings, canWrite, archived, and nullable RFC 3339 archivedAt. The id is the canonical dashboard workspace-file id; use it as the foreign key in extension-owned data. created is false when an idempotent create finds the same canonical project.

The Project API exposes the setProjectArchived(args) helper when projects_set_archived is advertised. It requires projects_write and a dashboard that advertises shared-projects-archive-v1:

js
if (appInfo.projects?.commands?.includes('projects_set_archived')) {
  const archived = await bridge.setProjectArchived({
    projectId: result.project.id,
    archived: true
  });

  await bridge.setProjectArchived({
    projectId: archived.project.id,
    archived: false
  });
}

The response contains the updated project and an idempotent changed flag. Setting the current state again returns changed: false. Archive and restore never change the project id or remove extension-owned data; restoring makes the same identity active again.

The host injects the frozen workspaceId and groupId; iframe values for those fields are discarded. Project registry folders are system-owned, read-only identity records. They are not extension storage, so use ensureGroupFolder() for JSON, Markdown, transcripts, and other extension-owned project content.

Audio And Transcription Helpers ​

Discover the runtime surface before using it:

js
const bridge = window.isaExtensionBridge;
const appInfo = await bridge.getAppInfo();

if (!appInfo.audioCapture?.enabled || !appInfo.transcription?.enabled) {
  // Render an unavailable state instead of starting the workflow.
}

audioCapture and transcription expose their commands and events. Transcription also reports that the current API is batch-only, has no live transcription, and can transport optional speaker labels. This host-level capability does not mean every transcription model separates speakers. Treat supportsSpeakerLabels on each transcription_list_providers descriptor as the authoritative model-specific value.

The bridge provides convenience methods for the canonical commands:

  • listAudioInputDevices()
  • getActiveRecording() and getRecording(args)
  • listRecordings()
  • startRecording(args), pauseRecording(args), resumeRecording(args)
  • stopRecording(args), cancelRecording(args), deleteRecording(args)
  • listTranscriptionProviders()
  • startTranscription(args), getTranscription(args)
  • cancelTranscription(args), deleteTranscription(args)

Recording methods exchange opaque IDs and safe state only. They never return a path, blob URL, base64, or audio bytes. The host injects and overwrites workspaceId, groupId, and extensionId for every scoped media command. An extension passes only workflow inputs such as deviceId, recordingId, modelId, and jobId; it cannot select or widen its own authorization scope.

js
const devices = await bridge.listAudioInputDevices();
const recording = await bridge.startRecording({
  deviceId: devices.find((device) => device.isDefault)?.deviceId
});

await bridge.stopRecording({ recordingId: recording.recordingId });

const providers = await bridge.listTranscriptionProviders();
if (!providers.length) throw new Error('No speech-to-text model is available to this group');

const started = await bridge.startTranscription({
  recordingId: recording.recordingId,
  modelId: providers[0].id,
  language: 'nl'
});

const unsubscribe = bridge.on('transcription-completed', (event) => {
  if (event.jobId === started.jobId) void bridge.getTranscription({ jobId: started.jobId });
});

// Retain this callback and call it when the component or extension view is disposed.
const dispose = () => unsubscribe();

The first start for a local model may initiate its host-managed download and reject temporarily. A production flow must handle that handshake, persist the job ID, reconcile missed events, and clean up in the right order. See the complete audio and transcription quick start.

Bridge Events ​

Current event names include:

  • app:ready
  • filesystem-upload-progress
  • stream-chat-token
  • stream-chat-summarization-needed
  • chat-models:changed
  • theme:changed
  • audio-capture-state-changed
  • transcription-progress
  • transcription-segment
  • transcription-completed
  • transcription-failed
  • filesystem-sync-state-changed
  • bridge:script-loaded-v2
  • bridge:window-error
  • bridge:unhandled-rejection

ISA Warden extension specification