c946b73d11
Remote clients (docs/plans/remote-clients/README.md, B1–B3 for the Claude pool): - server/hub/ holds the hub-only code (devices, remote sessions, upload ingest, LAN gateway), required only in server mode; hub-boundary.test.js keeps local code from importing it. - A local install enrolls with `agenthub remote login` or Settings → AI tools → Company hub; the "Company account" AI Proxy (reserved name agenthub.company) sends a session through the hub's gateway, which adds a pool account's token — no credential reaches the machine. Only those sessions are uploaded, hourly and at their end; secret-shaped strings are redacted on the client. - Gateway refuses: unknown device (401), a message without a session id, an unregistered session under AGENTHUB_REMOTE_GATE=block, a user over quota (EQUOTA), a purged session (410). Device tokens 90 days, conversations 30. - New alert rule type remote_unmonitored: a company-account session its device never registered; a late registration clears the flag instead. - The hub imports a session once per upload round (chunks carry `more`), not once per chunk. Artifacts: - Share an artifact with chosen people (artifact_shares, PATCH sharedWith, GET /api/artifacts/people) besides everyone; a "Shared with you" tab. Only the owner or an admin sees or edits who it is shared with; audited. - Folders: each account's own arrangement (agenthub.artifacts.folders pref). - A claude.ai page keeps a copy viewable without the publishing account: its HTML file (and supporting files) snapshotted at publish, or the copy Claude saves for a Doc / Slides / Design in artifacts-export/ (a synchronous PostToolUse hint hook, scripts/artifact-export-hint.js, admin switch), attached to the page at Stop. Viewed in HtmlFrame's sandbox, Markdown, or pdf.js (pdfjs-dist 6.4.299, legacy build, loaded only for a PDF). Fixes: - tmux: a cached /proc/<pid>/exe of an exited server falls back to PATH's tmux (stop the last pane, start again within 10 s failed with ENOENT). - shelves: a shelf restores beside changes in other files. - chat: a finished reply ends the turn even with no hook at all. - Settings → AI tools: profile list display tweaks.
Artifacts hub — plan
Drafted 2026-10-14. Status: all three phases built 2026-10-14 (the session view's own Artifacts tab is not built; the page has the per-workspace file picker).
Goal
One place to keep what agents PRODUCE, and (in server mode) to share it:
| Kind | What it is | Where it comes from |
|---|---|---|
page |
A claude.ai Artifact a session published (claude.ai/…/artifact/…) |
the Artifact tool's (or a Claude Docs doc-creating batch's) PostToolUse hook, or a link pasted by hand |
file |
A file an agent wrote (HTML, Markdown, PDF, image, CSV, Office…) | Write/Edit/NotebookEdit hook events, kept on the user's pin; a pptx/docx/xlsx/pdf/html the session created or changed, captured at its Stop (auto); or uploaded from the computer |
skill |
A Claude Code skill folder (SKILL.md + files) |
the user's own ~/.claude/skills/<name> or a workspace's .claude/skills/<name>; or uploaded (a Markdown file, files, a folder, a .zip/.skill — several skills at once too) or written in the dialog, under a name the user chooses |
plugin |
A plugin by marketplace source + name (no files) | picked from the list a marketplace link answers (POST /api/artifacts/plugin/inspect), or typed as name@marketplace |
- Local mode: one personal library, no sharing UI.
- Server mode: every artifact is the OWNER's and private by default. The owner can Share it with everyone — it appears in the Hub for every signed-in user — or with chosen people only (
artifact_shares; they find it under Shared with you). Apagekeeps a viewable copy — its HTML file, or the copy Claude exports of a Doc / Slides / Design (artifacts-export/) — so the people it is shared with need no access to the claude.ai account that published it. A shared skill or plugin can be installed into the viewer's own account.
Rules (binding, same spirit as CLAUDE.md)
- What is shared is a SNAPSHOT, never the owner's live file. Sharing a
file/skillcopies it (size-capped, regular files only, no symlinks followed) into<data dir>/artifacts/<id>/, written once likesession_snapshots. Viewers read only that copy, so no route ever reads another user's home for them. Re-sharing makes a new snapshot. - Nothing shared runs on the dashboard's origin. HTML/SVG previews are served under the
/git/rawheaders (sandboxCSP +nosniff) and shown in a sandboxed iframe; a type that sandbox cannot show (Office files, zip, PDF — the sandbox blocks both a download and the PDF viewer) gets Download only, never a blank Preview. Apageis only a link out, accepted only ashttps://claude.ai/…. - Installing is the viewer's explicit click, into the viewer's own account only. For a skill, the hub writes the snapshot's files into
~/.claude/skills/<name>/of the VIEWER (S3: as their member, throughwrite-as). It never overwrites an existing skill withoutconflict: replace, and the old one goes to the trash first. For a plugin, the hub runsclaude plugin marketplace add <source>+claude plugin install <name>@<market> --scope useras the viewer (theapplyPluginBaselinepath). The marketplace source passes an allowlist (owner/repoor avalidateRemoteUrlURL), and no argv comes from the request beyond those two validated values. - Before installing, the dialog lists every file of the skill (a skill may carry scripts that the viewer's agent will run). Every share, unshare and install is written to
audit_log. - Visibility follows ownership: a developer sees their own artifacts plus those shared with everyone or with them by name; admin/analyst see per
scope.js, like sessions. Unshare removes an artifact from the Hub but does not uninstall copies others already installed.
Data
New table artifacts (CREATE TABLE IF NOT EXISTS in server/db.js):
id, kind, title, description, owner_id, lane_id, session_id, source (path | url | market spec), url, snapshot (dir name | NULL), size, shared (0/1), shared_at, installs, created_at, updated_at, plus an index on (owner_id, kind) and on (shared, kind).
Phases
- Library (local + server, private). Table,
server/lib/artifacts.js,/api/artifacts(list/get/create/patch/delete).pagerows are captured from theArtifactPostToolUse hook (URL taken fromtool_responseby anhttps://claude.ai/…artifact/…match).filecandidates are listed per workspace from Write events with a document extension, and the user pins them. New Artifacts page (sidebar) plus an Artifacts tab in the session view's right panel. Scope wiring:PATH_RULES,broadcastAudience. - Hub (server mode). Share/Unshare buttons, snapshot,
Hubtab, sandboxed preview, download,artifact_sharedbroadcast. - Skills & plugins. "Share a skill/plugin" picker, the install flow (S3 included), conflict handling, audit.
Each phase ships with its server and client tests and an update to docs/API.md.
Open questions
- No
Artifacttool call has happened on this machine yet, so the exact hook payload of a publish is unverified. Phase 1 matches the URL loosely and keeps "paste a link" as the fallback. - Should an admin be able to disable Hub installs for everyone (one switch in Settings)? Proposed: yes, on by default.