---
title: Artifact Sharing (shareable HTML of sessions, messages, selections)
---

# Artifact Sharing (shareable HTML of sessions, messages, selections)

## What it is

Artifact Sharing turns any session, message, or highlighted selection into a **self-contained shareable HTML page** hosted publicly. You right-click → **Share**, pick a scope, pick an expiration, and Omniscio generates a URL that renders the content — fully styled, with syntax highlighting, light/dark theme support, and arrow-key navigation between messages — in anyone's browser without them needing Omniscio or an account. Useful for showing a teammate what an agent did, sending a code walkthrough, or archiving a conversation. The URL uses a random 64-char hex token (32 random bytes, 256-bit entropy) so it's only guessable to the degree of the token entropy, and expiration is enforced server-side (expired shares return 410 Gone, not just a stale page).

## Where to find it

### How to use it

1. **Share a whole session.** Click the overflow menu on any session → **Share**. The share dialog opens with **Conversation** selected. Pick an expiration — **1 hour**, **24 hours**, **7 days**, or **30 days** (all public shares must carry a bounded expiry). Click **Create link**. A URL like `https://…/s/<64-hex-token>` is copied to your clipboard.
2. **Share a single message.** Right-click any message bubble → **Share**. The dialog shows a **This message** radio option (only visible when a messageId is provided). Pick it → confirm → copied.
3. **Share a selection.** Highlight any text in the conversation, right-click the selection → **Share**. The dialog shows a **Selected text** radio option. Good for sending just the code snippet you were discussing without the conversation wrapper.
4. **Manage your shares.** Open the **Shares** entry in the left sidebar (Omniscio group) to list everything you've published: scope, expiration, view count, Protected badge for password-gated links. You can revoke any share manually (immediate 404), delete expired ones to clean up, or click **Edit** on an active share to change its label, expiration, password, view cap, or notify-on-view setting — full UX in [shares-view.md § Editing a share](shares-view.md).
5. **Live-updating (preview).** Each share has a **Live-updating** checkbox — when on (implementation in progress), the published HTML re-syncs from the session so viewers always see the latest state. Otherwise it's a snapshot of the moment you created the link.
6. **Protect a share.** From the same Edit modal you can add a password, cap the number of views, or turn on a desktop notification fired the first time the share is opened. Passwords are PBKDF2-SHA256-hashed in your machine (600,000 iterations, fresh 16-byte salt per share) — plaintext never leaves the renderer. View cap, view count, last-viewed timestamp, and notify-on-view are mirrored back from the Cloud Function via the 60-second view-event poller. See [share-view-events.md](share-view-events.md) for the privacy model and [shares-view.md § How protection enforcement works](shares-view.md) for the end-to-end flow.

## How it behaves

### How it works

The HTML builder is [/src/main/services/share/share-engine.ts](/src/main/services/share/share-engine.ts) — it collects the selected scope's conversation, renders markdown + syntax highlighting with highlight.js, and inlines all CSS for both light and dark themes (so the page looks right regardless of the viewer's prefers-color-scheme). The page mirrors the Omniscio app's chat look — glass-depth (3D) message bubbles, each headed by the app's own role icon (lucide **User** for you, **Bot** for the agent), drawn as CSS-masked SVGs (the same technique as the Dark Mode toggle) so they need no script and stay CSP-safe. **Assistant messages are shared as prose only** — the mechanical `▸`/`←` tool-call/result lines are stripped fence-aware (a `▸` shown inside a code block survives), and the agent's `[[OMNISCIO_FINAL]]` final-answer marker is honored: when a message carries it, only the prose after it is shared and the working notes above are dropped; the raw marker never appears, and a turn that was pure tool activity is skipped rather than rendered as an empty bubble (`renderAssistantContent` reuses the same `splitOnFinalBoundary` + `stripToolLinesFenceAware` the on-screen renderer and the visible-only [Markdown export](copy-session-as-markdown.md) use, so a share matches what you see in Omniscio). It also embeds two tiny inline scripts: a **theme-toggle** — the header **Dark Mode** button that flips the page light/dark and remembers the choice in the viewer's browser — and **keyboard navigation**, where **Arrow** (or **Shift+Arrow**) moves a highlight to the previous/next message and scrolls it into view (mouse/trackpad scrolling, OS shortcuts like Cmd/Ctrl+Arrow, and typing in the comment box are all left alone; it no-ops on a single-selection share). The served page's CSP otherwise runs no script, so each is allowed only by its exact SHA-256 hash, pinned in the Cloud Function's `SHARE_CSP` (see [share-csp-theme-script-contract.md](../../.claude/memory/contracts/share-csp-theme-script-contract.md)); expiration is enforced purely server-side via the Firestore mirror (no `share-expires` meta tag in the published HTML). No external stylesheets, no fonts fetched at render time — entirely self-contained. The publisher is [/src/main/services/share/share-publisher.ts](/src/main/services/share/share-publisher.ts), which uploads the HTML to Firebase Cloud Storage at `shares/{token}.html` in the bucket `agentmc-shares-artifacts`. The 16-character hex token is the URL's path segment; there's no separate authentication layer — the token is the credential. The Firebase Cloud Function [/firebase/functions/src/index.ts](/firebase/functions/src/index.ts) exports `serveShare` at path `/s/:token`, which: (1) fetches the HTML blob, (2) reads the expiration timestamp from its meta tag, (3) if expired, deletes the blob and returns 410 Gone; otherwise returns the HTML with the right content-type headers. IPC surface is create/list/revoke/delete via [/src/main/ipc/share-handlers.ts](/src/main/ipc/share-handlers.ts); the dialog UI is [/src/renderer/src/features/shares/ShareDialog.tsx](/src/renderer/src/features/shares/ShareDialog.tsx). Persistence + audit trail in [/src/main/db/queries-share.ts](/src/main/db/queries-share.ts). 709 tests keep the HTML rendering + expiration + token collision behavior pinned down.

## Related

- [shares-view.md](shares-view.md) — the top-level sidebar view that lists / edits / revokes / deletes every share you've created
- [share-view-events.md](share-view-events.md) — privacy model + audit-log shape for the view-tracking pipeline (counts, notify-on-view, poller mechanics)
- [share-artifacts.md](share-artifacts.md) — the sibling feature for publishing local files + pasted content
- [use-recipes.md](use-recipes.md) — share the output of a recipe run as an artifact
- [cli-control.md](cli-control.md) — the same conversation is also reachable via `omniscio://session/<id>` for Omniscio users (private)
