artifacty 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  This file provides guidance to Claude Code when working with this repository.
4
4
 
5
- Artifacty is a local, agent-to-agent artifact exchange for LLM workflows. One agent publishes an artifact once; others list, read, import, update, and continue from it without copying content through chat. It exposes one shared store over HTTP/browser, CLI, and MCP stdio surfaces.
5
+ Artifacty is a local, agent-to-agent artifact exchange for LLM workflows. One agent publishes an artifact. Other agents can list, read, import, update, and continue from it without copying content through chat. HTTP, the browser, the CLI, and MCP stdio use the same store.
6
6
 
7
7
  See `AGENTS.md` for repository structure, coding-style conventions, and commit/PR guidelines.
8
8
 
9
+ For English and Korean documentation, follow the [writing style guide](docs/writing-style.md). Use clear sentences and consistent terms. Keep technical vocabulary precise. Write Korean in natural, fluent Korean rather than translating English sentence patterns.
10
+
9
11
  ## Commands
10
12
 
11
13
  - `npm test` — run all tests with `node --test`.
@@ -16,7 +18,7 @@ See `AGENTS.md` for repository structure, coding-style conventions, and commit/P
16
18
  - `node src/mcp-server.js` or `npm run mcp` — run the MCP stdio server.
17
19
  - `node src/cli.js help` — inspect CLI usage.
18
20
 
19
- Run `npm run release:check` before claiming a release-ready change. There is no build step; this is plain ESM run directly by Node `>=22.5`.
21
+ Run `npm run release:check` before claiming a release-ready change. There is no build step; this is plain ESM run directly by Node `>=22.12.0`.
20
22
 
21
23
  ## Architecture
22
24
 
@@ -39,7 +41,9 @@ The store lives at `ARTIFACTY_HOME` or `~/.artifacty` by default.
39
41
  - `server.json` records the currently running browser server URL so CLI and MCP responses keep working when the default port falls back.
40
42
  - Legacy `index.json` stores migrate automatically on first access.
41
43
 
42
- Versions are append-only. Do not mutate prior content files, except through the audited admin repair (`replaceArtifactVersion`) and delete (`deleteArtifactVersion`) paths, which exist specifically to correct bad historical versions and record every change in the audit log.
44
+ Versions are append-only. To correct a historical version, use the admin repair (`replaceArtifactVersion`) or delete (`deleteArtifactVersion`) path. These paths record every change in the audit log. Do not modify prior content files through other paths.
45
+
46
+ Before changing upgrade behavior, read [upgrade snapshots and recovery](docs/upgrade-backups.md). `src/lib/upgrade-backup.js` protects schema migrations with a mandatory verified snapshot. Optional application-update snapshots use `ARTIFACTY_BACKUP_ON_UPDATE=true`. Keep recovery commands independent of normal storage initialization so they cannot trigger migration. Stop every server and MCP process before restoring a store. Test recovery only with temporary stores.
43
47
 
44
48
  ### Security Model
45
49
 
@@ -51,7 +55,9 @@ Versions are append-only. Do not mutate prior content files, except through the
51
55
 
52
56
  ### Browser Editor
53
57
 
54
- The create/import/edit screens use CodeMirror 6 for Markdown, HTML, JSON, and text editing. CodeMirror assets are served locally through allowlisted `/vendor/npm/*` routes, not from a CDN. UI text defaults to English and supports Korean with `?lang=ko`; documentation stays English-only.
58
+ The create/import/edit screens use CodeMirror 6 for Markdown, HTML, JSON, and text editing. CodeMirror assets are served locally through allowlisted `/vendor/npm/*` routes, not from a CDN. UI text defaults to English and supports Korean with `?lang=ko`. Keep each document in its existing language unless a translation is requested.
59
+
60
+ Mermaid uses version 12.1.0 with the existing `classic` look, `dagre` layout, and `default` theme. Its browser code requires ES2024 support, such as Safari 17.4 or newer. The optional Babel 8 React renderer requires Node `^22.18.0 || >=24.11.0`.
55
61
 
56
62
  ### MCP Server
57
63
 
package/README.md CHANGED
@@ -3,19 +3,24 @@
3
3
  [![Publish](https://github.com/raeseoklee/artifacty/actions/workflows/publish.yml/badge.svg)](https://github.com/raeseoklee/artifacty/actions/workflows/publish.yml)
4
4
  [![npm version](https://img.shields.io/npm/v/artifacty.svg)](https://www.npmjs.com/package/artifacty)
5
5
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
- [![Node.js >=22.5](https://img.shields.io/badge/node-%3E%3D22.5-339933.svg)](package.json)
6
+ [![Node.js >=22.12.0](https://img.shields.io/badge/node-%3E%3D22.12.0-339933.svg)](package.json)
7
7
 
8
- Artifacty is a local, agent-to-agent artifact exchange for LLM workflows. Claude, Codex, Gemini, GitHub Copilot, Cursor, and other MCP-capable tools can publish an artifact once, then other agents can list, read, update, and continue from it without copying content through chat.
8
+ Artifacty gives AI-generated artifacts a local home that agents can share. Claude, Codex, Gemini, GitHub Copilot, Cursor, and other MCP tools can publish artifacts to the same store. Other agents can find, read, and update them without copying content through chat.
9
9
 
10
10
  ![Artifacty overview showing multiple AI agents sharing artifacts through a local exchange](docs/assets/artifacty.png)
11
11
 
12
12
  ## Why MCP
13
13
 
14
- Claude Code artifacts are useful because they turn session output into shareable, versioned pages. Artifacty keeps that local and cross-agent: the browser server renders artifacts for people, while the MCP stdio server gives agents a common tool interface.
14
+ Artifacts turn session output into shareable, versioned pages. Artifacty keeps those pages local and makes them available across agents. The browser server renders artifacts for people. The MCP stdio server gives agents a common tool interface.
15
15
 
16
16
  ## Installation
17
17
 
18
- Artifacty requires Node.js 22.5 or newer.
18
+ Artifacty requires Node.js 22.12.0 or newer.
19
+
20
+ The optional React renderer uses Babel 8. Its supported Node.js versions are
21
+ `^22.18.0 || >=24.11.0`. Use a compatible version if you enable that renderer.
22
+
23
+ The browser renderer uses Mermaid 12.1.0. Use a current browser with ES2024 support, such as Safari 17.4 or newer. Mermaid keeps the `classic` look, `dagre` layout, and `default` theme.
19
24
 
20
25
  Install the CLI globally from npm:
21
26
 
@@ -36,7 +41,7 @@ Start the local dashboard in the background:
36
41
  artifacty serve
37
42
  ```
38
43
 
39
- Open the `url` printed in the JSON response. Artifacty prefers `http://127.0.0.1:8787`; if that default port is busy and no explicit port was configured, it starts on the next available local port and records the actual URL for CLI and MCP responses.
44
+ Open the `url` in the JSON response. Artifacty prefers `http://127.0.0.1:8787`. If that port is busy and you did not configure a port, Artifacty uses the next available local port. It records the actual URL for CLI and MCP responses.
40
45
 
41
46
  Manage the background server:
42
47
 
@@ -45,8 +50,8 @@ artifacty status
45
50
  artifacty stop
46
51
  ```
47
52
 
48
- `artifacty start` and `artifacty serve --detach` use the same lifecycle path as `artifacty serve`. Logs are written under `~/.artifacty/logs/`.
49
- These lifecycle commands use Node's detached process support and work on macOS, Linux, and Windows. `artifacty stop` uses process-group signals on macOS/Linux and Windows `taskkill`, falling back to `/F` when Windows requires forceful termination.
53
+ `artifacty start` and `artifacty serve --detach` use the same lifecycle path as `artifacty serve`. Artifacty writes logs under `~/.artifacty/logs/`.
54
+ These commands use Node's detached process support on macOS, Linux, and Windows. `artifacty stop` uses process-group signals on macOS and Linux. On Windows, it uses `taskkill`. If Windows requires forceful termination, it retries with `/F`.
50
55
 
51
56
  For foreground debugging, keep the process attached:
52
57
 
@@ -174,7 +179,7 @@ them as HTML instead of plain text.
174
179
 
175
180
  ## Agent Handoff Example
176
181
 
177
- One agent can publish a continuation artifact, then another agent can discover it, read the context, and append the next version.
182
+ One agent publishes a handoff artifact. Another agent finds it, reads the context, and appends the next version.
178
183
 
179
184
  Codex publishes the handoff:
180
185
 
@@ -259,7 +264,9 @@ By default Artifacty stores files under `~/.artifacty`.
259
264
  ARTIFACTY_HOME=/path/to/shared/store artifacty serve
260
265
  ```
261
266
 
262
- Artifact metadata is stored in `artifacty.sqlite`; artifact content is stored as append-only version files under `artifacts/` for normal create and update flows. Administrators can repair or delete individual bad versions from the browser, and those exceptional actions are recorded in the audit log. The current browser server URL is written to `server.json` so MCP tools can return the correct links when the default port falls back. Existing `index.json` stores are migrated automatically on first access.
267
+ Artifacty stores metadata in `artifacty.sqlite`. Normal create and update operations append version files under `artifacts/`. Administrators can repair or delete individual versions from the browser. The audit log records these exceptions.
268
+
269
+ Artifacty writes the current browser server URL to `server.json`. MCP tools use this URL to return correct links when the server selects another port. On first access, Artifacty automatically migrates existing `index.json` stores.
263
270
 
264
271
  Every artifact row carries a `visibility` (`private`/`team`, default `team`) and an `ownerUserId`, backfilled from the artifact's `publisherUserId` on migration. See the Security Model section below for the access rules these enforce.
265
272
 
@@ -286,13 +293,23 @@ as artifacts-only. For large migrations or scripted server moves, use
286
293
  `artifacty backup --full` and
287
294
  `artifacty import-store --file ./artifacty-backup.json --confirm replace-all`.
288
295
 
289
- Search uses a SQLite FTS5 index when the local Node SQLite build supports it. The index covers the latest version body plus title, tags, source agent, artifact type, format, and metadata summary. If FTS5 is unavailable, Artifacty keeps working with metadata search. Rebuild or check the store when needed:
296
+ If the local Node SQLite build supports FTS5, search uses a SQLite FTS5 index. It covers the latest version body, title, tags, source agent, artifact type, format, and metadata summary. If FTS5 is unavailable, Artifacty uses metadata search. To rebuild or check the store, run:
290
297
 
291
298
  ```bash
292
299
  artifacty index rebuild
293
300
  artifacty integrity
294
301
  ```
295
302
 
303
+ ### Upgrade Snapshots
304
+
305
+ Before an existing store needs a schema migration, Artifacty creates and verifies a mandatory local snapshot. The snapshot includes the raw SQLite database, referenced version files, and legacy index. If snapshot verification fails, migration stops.
306
+
307
+ Set `ARTIFACTY_BACKUP_ON_UPDATE=true` to also snapshot application-version changes. If an existing store has no recorded application version, its first open with this setting creates a baseline snapshot. `ARTIFACTY_UPGRADE_BACKUP_KEEP` retains the latest three automatic snapshots by default (valid range: 1–1000).
308
+
309
+ Use `artifacty backups list`, `artifacty backups verify <id>`, and `artifacty backups restore <id> --confirm restore-store`. Stop every server and MCP process before an upgrade or restore. Restore creates a recovery snapshot before replacing the store. It also restores credentials and security state; previously revoked sessions, tokens, and links can become valid again.
310
+
311
+ These local snapshots are separate from portable `artifacty backup`/`export` JSON bundles. See [upgrade snapshots and recovery](docs/upgrade-backups.md) for verification, corrupt-store recovery, and interrupted-restore procedures.
312
+
296
313
  ### Semantic Search
297
314
 
298
315
  `?mode=keyword|semantic|hybrid` on `GET /api/artifacts`, the dashboard, `artifacty list --mode`, and the MCP `artifacty_list` tool add optional natural-language search on top of FTS5, without any bundled model or mandatory dependency:
@@ -314,7 +331,7 @@ export ARTIFACTY_EMBEDDINGS_API_KEY=sk-...
314
331
  export ARTIFACTY_EMBEDDINGS_COMMAND="python3 embed.py"
315
332
  ```
316
333
 
317
- The API key is read from the environment only; it is never logged, returned in an error, or persisted to the store (`artifacty doctor` reports the provider and model with the key redacted). New artifacts are embedded automatically in the background after create/update, and existing ones can be (re-)embedded in batches of 16 with:
334
+ Artifacty reads the API key only from the environment. It does not log the key, return it in errors, or save it to the store. `artifacty doctor` reports the provider and model with the key redacted. After a create or update, Artifacty generates embeddings in the background. To generate or refresh embeddings for existing artifacts in batches of 16, run:
318
335
 
319
336
  ```bash
320
337
  artifacty index rebuild --embeddings
@@ -322,7 +339,7 @@ artifacty index rebuild --embeddings
322
339
 
323
340
  ### Relations
324
341
 
325
- Artifacts can be linked with typed, directional relations so a handoff, its review, and follow-up work stay discoverable from one another:
342
+ Use typed, directional relations to connect a handoff, its review, and follow-up work:
326
343
 
327
344
  - Relation names are a closed set for v1: `derived-from`, `supersedes`, `reviews`, `references`, `part-of`.
328
345
  - Every relation has a computed inverse (`derived-from` ↔ `derives`, `supersedes` ↔ `superseded-by`, `reviews` ↔ `reviewed-by`, `references` ↔ `referenced-by`, `part-of` ↔ `contains`) shown automatically on the other side.
@@ -339,12 +356,12 @@ artifacty unlink <from-id> derived-from <to-id>
339
356
 
340
357
  ### Comments and review status
341
358
 
342
- Lightweight, version-anchored feedback without publishing a whole new version or a separate review artifact:
359
+ Add feedback to a specific version without publishing a new version or a separate review artifact:
343
360
 
344
- - Comments are Markdown, rendered through the same sanitized Markdown pipeline as artifact content, and capped at 16 KB. `anchor` is an optional, format-specific rendering hint — `{ line: 42 }` for text formats, `{ path: "$.runs[0].results[3]" }` for JSON/SARIF, `{ row: 7 }` for CSV — and is not validated against the artifact's actual content.
361
+ - Comments use the same sanitized Markdown renderer as artifact content. Each comment has a 16 KB limit. The optional `anchor` field provides a display hint: `{ line: 42 }` for text, `{ path: "$.runs[0].results[3]" }` for JSON/SARIF, or `{ row: 7 }` for CSV. Artifacty does not validate the hint against the content.
345
362
  - Threads are one level deep: `parentId` on a new comment must point to a root comment (one with no `parentId` of its own).
346
363
  - Deleting a comment is a soft delete: it disappears from `artifacty comments`/`GET .../comments` but its audit-log entry is kept. Pass `--include-deleted`/`includeDeleted=true` to see it anyway.
347
- - Every artifact carries a `reviewStatus`: `none`, `pending`, `changes-requested`, or `approved`, set explicitly and reset to `pending` automatically (noted in that update's audit metadata) whenever a new version is appended after an `approved` status.
364
+ - Each artifact has a `reviewStatus`: `none`, `pending`, `changes-requested`, or `approved`. Set the status explicitly. If you append a version to an approved artifact, Artifacty resets the status to `pending`. The update's audit metadata records this change.
348
365
  - `GET`/`POST /api/artifacts/:id/comments`, `POST /api/artifacts/:id/comments/:commentId/resolve`, `DELETE /api/artifacts/:id/comments/:commentId`, and `POST /api/artifacts/:id/review-status` manage comments and review status over HTTP.
349
366
  - `artifacty_comment`, `artifacty_resolve_comment`, and `artifacty_set_review_status` are the MCP equivalents; `artifacty_get` accepts `includeComments: true` to return the requested version's open comments alongside the artifact.
350
367
  - The browser artifact page shows a comments panel grouped by version, with a reply form, a resolve button, and (for the artifact's owner or an admin) a review-status selector. Line-anchored comments are shown as plain text (e.g. "line 42") rather than a CodeMirror gutter marker.
@@ -358,7 +375,9 @@ artifacty review-status release-handoff-abc12345 approved
358
375
 
359
376
  ### Diff
360
377
 
361
- `artifacty diff <id> [--from N] [--to M] [--structured] [--json]` compares two versions of an artifact, defaulting `--to` to the latest version and `--from` to `--to - 1`. JSON, SARIF, CSV, notebook, and bundle artifacts get a structured diff (JSON path entries, CSV row/cell entries, or per-file bundle entries) by default; other formats print a line diff. `--structured` forces the structured (word-highlighted line) diff for any format; `--json` prints the machine-readable form instead of the human-readable unified text.
378
+ `artifacty diff <id> [--from N] [--to M] [--structured] [--json]` compares two artifact versions. By default, `--to` selects the latest version and `--from` selects `--to - 1`.
379
+
380
+ JSON, SARIF, CSV, notebook, and bundle artifacts use a structured diff by default. The output contains JSON paths, CSV rows and cells, or bundle files. Other formats use a line diff. To request a structured diff for any format, use `--structured`; text lines include word highlights. To print machine-readable output instead of unified text, use `--json`.
362
381
 
363
382
  ```bash
364
383
  artifacty diff release-handoff-abc12345
@@ -367,14 +386,16 @@ artifacty diff release-handoff-abc12345 --from 1 --to 3 --json
367
386
 
368
387
  ### Retention
369
388
 
370
- Artifacts, audit rows, and events grow without bound by default. A declarative retention policy, stored in the `meta` table and edited through `/admin/retention` or `artifacty retention set`, lets an admin bound that growth:
389
+ By default, Artifacty does not limit retained artifacts, audit rows, or events. Administrators can set a retention policy at `/admin/retention` or with `artifacty retention set`. Artifacty stores the policy in the `meta` table:
371
390
 
372
391
  - `archiveAfterDays` (a default plus optional per-artifact-type overrides) auto-archives artifacts that have not been updated within the window, unless the artifact carries a tag in `keepTags` or has `reviewStatus: "approved"`.
373
392
  - `purgeArchivedAfterDays` hard-deletes artifacts that have been archived longer than the window, including their version files, dependent rows that cascade via foreign keys (comments, relations, embeddings), and their `events`/search-index rows. Audit log rows for the artifact are deliberately kept (see `auditRetentionDays` below) as the historical record that the purge happened, even though the artifact itself is gone. This is the only retention action that deletes data outright, so it is refused unless the operator has set `ARTIFACTY_RETENTION_ALLOW_PURGE=true` in the server environment; `--allow-purge` (or the `allowPurge` request field) only opts a single run in and never sets that environment variable itself. Always dry-run first (`artifacty retention run --dry-run`, or the report on `/admin/retention`) to review what would be purged.
374
393
  - `auditRetentionDays` prunes old audit log rows, except `version-repair`, `version-delete`, `retention-purge`, `retention-policy-update`, and `owner-change`, which are kept indefinitely.
375
394
  - `eventRetentionRows` caps how many rows the `events` change-notification table keeps.
376
395
 
377
- A background sweep runs every `ARTIFACTY_RETENTION_INTERVAL_MS` (default one hour) alongside the HTTP server, checking the current policy on every tick — it starts even when the policy is all-unset, so setting a policy later takes effect on the next tick without a restart. `artifacty integrity`/`checkStoreIntegrity` flags any files left behind if a purge is ever interrupted, and `artifacty doctor` reports the current retention policy summary.
396
+ The HTTP server runs a background sweep every `ARTIFACTY_RETENTION_INTERVAL_MS` (default one hour). Each sweep checks the current policy. The timer starts even if no policy is set. A later policy change takes effect on the next sweep without a restart.
397
+
398
+ If a purge is interrupted, `artifacty integrity`/`checkStoreIntegrity` reports files left behind. `artifacty doctor` reports the current retention policy summary.
378
399
 
379
400
  ```bash
380
401
  artifacty retention show
@@ -439,7 +460,9 @@ curl -s -X POST http://127.0.0.1:8787/api/webhooks \
439
460
  -d '{ "url": "https://example.com/hooks/artifacty", "eventTypes": ["artifact.updated"] }'
440
461
  ```
441
462
 
442
- The response includes `secret` once — store it; it is never returned again. Deliveries are `POST` JSON with headers `X-Artifacty-Event`, `X-Artifacty-Delivery`, and `X-Artifacty-Signature: sha256=<hmac>`, retried up to 3 times (2s/10s/60s backoff) and disabled after 20 consecutive failures. Manage webhooks at `/admin/webhooks` in the browser, or `GET/POST /api/webhooks`, `DELETE /api/webhooks/:id`, `POST /api/webhooks/:id/test`. See [docs/threat-model.md](docs/threat-model.md) for the signature scheme and SSRF guard.
463
+ Save `secret` from the response. Artifacty returns it only once. Each delivery sends JSON with `POST` and these headers: `X-Artifacty-Event`, `X-Artifacty-Delivery`, and `X-Artifacty-Signature: sha256=<hmac>`. Failed deliveries retry up to three times, after 2, 10, and 60 seconds. After 20 consecutive failures, Artifacty disables the webhook.
464
+
465
+ Manage webhooks at `/admin/webhooks` in the browser. The API provides `GET/POST /api/webhooks`, `DELETE /api/webhooks/:id`, and `POST /api/webhooks/:id/test`. See [docs/threat-model.md](docs/threat-model.md) for the signature scheme and SSRF guard.
443
466
 
444
467
  **MCP:** `resources/subscribe` on `artifacty://artifacts/{id}` or `artifacty://recent` delivers `notifications/resources/updated` over the stdio transport. The `artifacty_wait` tool (`{ artifactId?, tag?, type?, timeoutMs }`, max 120000ms) is a long-poll alternative that works over any transport, including the stateless `/mcp` HTTP transport where `resources/subscribe` cannot push notifications. See [docs/mcp-public-api.md](docs/mcp-public-api.md).
445
468
 
@@ -519,11 +542,13 @@ If the artifact's `latestVersion` no longer matches, the update is rejected with
519
542
  { "error": "Version conflict", "code": "version_conflict", "details": { "latestVersion": 4 } }
520
543
  ```
521
544
 
522
- `GET /api/artifacts/:id` sets an `ETag` header and returns `304 Not Modified` when the request's `If-None-Match` matches the current version. `expectedVersion` is optional, so clients that don't send it keep working exactly as before. The browser editor sends its own hidden `expectedVersion` field and shows a banner with a link to the diff view if another agent updated the artifact first.
545
+ `GET /api/artifacts/:id` sets an `ETag` header. If the request's `If-None-Match` matches the current version, the server returns `304 Not Modified`. The optional `expectedVersion` field preserves compatibility with clients that omit it.
546
+
547
+ The browser editor sends a hidden `expectedVersion` field. If another agent updated the artifact first, the editor shows a conflict banner with a link to the diff view.
523
548
 
524
549
  ## Interface Language
525
550
 
526
- The browser UI defaults to English. Add `?lang=ko` to any browser route to use Korean, for example `http://127.0.0.1:8787/new?lang=ko`. Forms and in-app links preserve the selected language. Documentation is maintained in English only.
551
+ The browser UI defaults to English. Add `?lang=ko` to any browser route to use Korean, for example `http://127.0.0.1:8787/new?lang=ko`. Forms and in-app links preserve the selected language. The current product guides are in English. English and Korean documentation follow the [documentation writing guide](docs/writing-style.md). Korean prose should use natural Korean phrasing.
527
552
 
528
553
  Schema and storage:
529
554
 
@@ -541,14 +566,127 @@ Schema and storage:
541
566
  - See [docs/sarif-csv-artifact-plan.md](docs/sarif-csv-artifact-plan.md) for the SARIF/CSV output artifact roadmap.
542
567
  - See [docs/roadmap-design.md](docs/roadmap-design.md) for the design of planned features such as relations, change notifications, optimistic concurrency, comments, semantic search, visibility, token scopes, and retention.
543
568
 
569
+ ## Public and Private Access Modes
570
+
571
+ This feature upgrades the SQLite store to version 10. Older builds reject the
572
+ upgraded store rather than ignoring its access policy.
573
+
574
+ Administrators can open **Account → Access** (`/admin/access`) to change the
575
+ server's access mode without restarting it. Existing installations default to
576
+ **public** mode, preserving anonymous dashboard access to team-visible artifacts;
577
+ artifacts marked private remain restricted to their owner and administrators.
578
+ API authentication and write protection still apply in public mode.
579
+
580
+ In **private** mode, browser visitors must sign in. An artifact is readable only
581
+ by its owner, an administrator, or users explicitly granted access, directly or
582
+ through a team. The existing `team` visibility value does not grant all users
583
+ access in this mode. Sharing grants support **read**, **comment**, and **edit**.
584
+ Commenters can add feedback and resolve their own comments; editors can also
585
+ append versions. The strongest direct or team grant applies. Ownership, sharing,
586
+ archiving, and review-status control remain with owners and administrators.
587
+ Administrators can access existing unowned artifacts. They can assign ownership
588
+ or share those artifacts.
589
+
590
+ Create teams on `/admin/access`. Select the members for each team.
591
+
592
+ To grant artifact access:
593
+
594
+ 1. Open the artifact as its owner or an administrator.
595
+ 2. Open **Sharing**.
596
+ 3. Select the allowed users and teams.
597
+ 4. Select their permission levels.
598
+ 5. Save the sharing settings.
599
+
600
+ The **Effective access** report shows each
601
+ user's actual permission and why it applies, anonymous access, and the global IP
602
+ condition. Removing a grant or team membership takes effect on subsequent reads,
603
+ search results, and change notifications. Personal API tokens carry the same
604
+ user permissions through HTTP and MCP. Shared server tokens have no user
605
+ identity and cannot read or create artifacts through ordinary artifact routes in
606
+ private mode. Trusted local CLI/store access remains an administrative
607
+ filesystem capability.
608
+
609
+ The same admin page accepts an optional IP/CIDR allowlist, one address or network
610
+ per line (for example `192.168.10.0/24` or `2001:db8:1234::/48`). If the list is empty,
611
+ all source addresses are allowed. If the list has entries, it restricts **all HTTP routes**
612
+ in both modes. This includes login, raw downloads, API routes, and HTTP MCP.
613
+ IP permission does not replace user or artifact permissions. Saving a list that
614
+ excludes the administrator's current connection address is rejected. Browser
615
+ changes are provisional for **120 seconds**: confirm that access still works to
616
+ keep the change, or roll it back. Unconfirmed changes revert before the next
617
+ policy check after the deadline, even across server restarts. A second change is
618
+ blocked while confirmation is pending. Mode changes offer a preview of how many
619
+ artifacts will become anonymously readable before you apply them.
620
+
621
+ Address checks use the actual TCP peer, including IPv4-mapped IPv6 addresses;
622
+ `X-Forwarded-For` and `Forwarded` are not trusted. Behind a reverse proxy, the
623
+ peer is the proxy: enforce end-user IP restrictions at that proxy. Local stdio
624
+ MCP and CLI operations do not have an HTTP source IP and are not covered by the
625
+ network allowlist.
626
+
627
+ Full backups include modes, network settings, teams, memberships, sharing grants,
628
+ and inbox entries. Pending network confirmation state and share-link tokens are
629
+ not exported. Artifact-only restores do not restore grants. If they replace existing grants,
630
+ they require confirmation. Restoring a full backup into a private server
631
+ keeps it private even if the backup was public, and an existing network policy
632
+ is preserved.
633
+
634
+ If you switch back to public mode, anonymous visitors can read team-visible
635
+ artifacts again. Artifacty retains explicit sharing grants for a later return
636
+ to private mode.
637
+
638
+ ## Share Links and Inbox
639
+
640
+ Owners and administrators can create a read-only link from an artifact's
641
+ **Sharing** page:
642
+
643
+ 1. Choose the latest version or pin a particular version.
644
+ 2. Set an expiration (seven days by default).
645
+ 3. Copy the URL when it is first shown.
646
+
647
+ Only a hash is stored, so the complete URL is not shown again. Links can be
648
+ revoked individually and are permanently revoked on ownership transfer.
649
+ Archived artifacts and deleted versions cannot be read through their links.
650
+
651
+ Links grant access to the selected artifact without an account, so private mode
652
+ requires an administrator to enable **Allow share links in private mode** at
653
+ `/admin/access`. Disabling this policy also disables existing links while the
654
+ server is private. Every link request still obeys the network allowlist. Links
655
+ do not expose the dashboard, history, comments, or editing controls. Public HTML
656
+ previews are static: scripts and external resources are disabled. React,
657
+ Mermaid, SVG and notebook shares show source instead of running active content.
658
+
659
+ The **Inbox** (`/inbox`) lists newly shared artifacts, comments, review requests,
660
+ and new versions for the signed-in user. Events caused by the same user are
661
+ excluded. Read individual notifications or mark all as read. Revoking artifact
662
+ access also hides its notifications and removes them from unread counts.
663
+
664
+ ## Sample Manuals
665
+
666
+ Install four English sample artifacts: **Start here**, **Feature guide**,
667
+ **Architecture**, and **Handoff exercise**. Administrators can use the sample
668
+ controls on `/admin/access`; local CLI users can run:
669
+
670
+ ```bash
671
+ artifacty samples install
672
+ artifacty samples remove
673
+ ```
674
+
675
+ Installation is idempotent within the installing account (or local CLI scope).
676
+ Reinstalling restores archived samples without overwriting edits. Removal
677
+ archives managed samples and preserves their versions; it does not delete other
678
+ artifacts with similar names or tags. In private mode, share the installed
679
+ manuals with the intended users or teams from each artifact's Sharing page.
680
+
544
681
  ## Security Model
545
682
 
546
683
  - The HTTP server binds to `127.0.0.1` by default.
547
684
  - If `ARTIFACTY_API_TOKEN` is set, HTTP API routes require `Authorization: Bearer <token>` or `x-artifacty-token`; scripts should prefer headers over `?token=...` URLs.
548
685
  - When users exist, personal API tokens issued from `/account` also authenticate HTTP API and MCP requests. Created artifacts record the token owner's email as `publisherId`, and audit logs record the same identity as `actor`.
549
686
  - API token checks use timing-safe digest comparison.
550
- - Personal API tokens carry scopes (`read`, `write`, `admin`; default `read`+`write`), settable as checkboxes when creating a token from `/account`. `admin` is only grantable to an admin user's token. Every `/api/*` route requires the matching scope on the authenticating token; the shared `ARTIFACTY_API_TOKEN` and browser sessions always have full scopes. A denial responds `403` with `code: "scope_denied"` and writes a rate-limited `token-scope-denied` audit row. Over MCP, `tools/list` omits mutating tools for a token without `write`, and calling one anyway returns an error result with `structuredContent.code: "scope_denied"`.
551
- - Once any user account exists, every artifact has a `visibility` (`private` or `team`, defaulting to `team`) and an `ownerUserId`. `team` is readable and writable by any authenticated principal (unless `ARTIFACTY_TEAM_WRITE=owner` restricts writes to the owner or an admin). `private` is readable and writable only by its owner or an admin; reads by anyone else 404 instead of 403 so the artifact's existence isn't leaked. Archiving, restoring, and changing visibility or ownership always require the owner or an admin, regardless of visibility. A shared `ARTIFACTY_API_TOKEN` (or any request before the first user account is created) has no personal identity and is treated as an anonymous team principal: it can read and write `team` artifacts but never sees `private` ones. In single-user mode (no user accounts at all) every check is bypassed, matching pre-visibility behavior. Change visibility and ownership with `POST /api/artifacts/:id/visibility` and `POST /api/artifacts/:id/owner`, the `artifacty visibility <id> private|team` CLI command, or the `artifacty_set_visibility` MCP tool.
687
+ - Personal API tokens support `read`, `write`, and `admin` scopes. The default is `read`+`write`. Select scopes when creating a token at `/account`. Only administrator tokens can receive `admin`. Each `/api/*` route requires the matching token scope. The shared `ARTIFACTY_API_TOKEN` and browser sessions have full scopes.
688
+ If a scope check fails, the server returns `403` with `code: "scope_denied"`. It also writes a rate-limited `token-scope-denied` audit row. For MCP tokens without `write`, `tools/list` omits tools that change data. Calls to those tools return `structuredContent.code: "scope_denied"`.
689
+ - Once any user account exists, every artifact has a `visibility` (`private` or `team`, defaulting to `team`) and an `ownerUserId`. In public access mode, `team` is readable and writable by any authenticated principal (unless `ARTIFACTY_TEAM_WRITE=owner` restricts writes to the owner or an admin). `private` is readable and writable only by its owner or an admin; reads by anyone else 404 instead of 403 so the artifact's existence isn't leaked. Archiving, restoring, and changing visibility or ownership always require the owner or an admin, regardless of visibility. A shared `ARTIFACTY_API_TOKEN` (or any request before the first user account is created) has no personal identity and is treated as an anonymous team principal: it can read and write `team` artifacts but never sees `private` ones. In single-user mode (no user accounts at all) every check is bypassed, matching pre-visibility behavior. Change visibility and ownership with `POST /api/artifacts/:id/visibility` and `POST /api/artifacts/:id/owner`, the `artifacty visibility <id> private|team` CLI command, or the `artifacty_set_visibility` MCP tool.
552
690
  - HTTP and MCP-over-HTTP requests are rate limited per `(token or user or remote address, bucket)` in fixed one-minute windows: `write` (mutating routes, default 120/min, `ARTIFACTY_RATE_WRITE_PER_MIN`), `auth` (`/login`, default 10/min per address, `ARTIFACTY_RATE_AUTH_PER_MIN`), and `search` (`GET /api/artifacts?q=`, default 300/min, `ARTIFACTY_RATE_SEARCH_PER_MIN`). Rate limiting is disabled on a loopback bind unless `ARTIFACTY_RATE_LIMIT=always`, and always disabled with `ARTIFACTY_RATE_LIMIT=off`. Exceeding a limit returns `429` with a `Retry-After` header, `code: "rate_limited"`, and a rate-limited `rate-limited` audit row.
553
691
  - Binding outside localhost requires both `ARTIFACTY_SHARE_MODE=lan` or `team` and `ARTIFACTY_API_TOKEN`.
554
692
  - Non-local sharing is intended for trusted LAN or VPN sessions. Prefer a specific interface IP over `0.0.0.0`, keep React rendering disabled, and see [docs/network-sharing.md](docs/network-sharing.md).
@@ -574,11 +712,13 @@ See [SECURITY.md](SECURITY.md), [docs/threat-model.md](docs/threat-model.md), an
574
712
 
575
713
  ## Environment Variables
576
714
 
577
- Every `ARTIFACTY_*` variable Artifacty reads, most already covered in context above. `node src/cli.js help` prints the same list.
715
+ The table lists every `ARTIFACTY_*` variable that Artifacty reads. `node src/cli.js help` prints the same list.
578
716
 
579
717
  | Variable | Purpose | Default |
580
718
  | --- | --- | --- |
581
719
  | `ARTIFACTY_HOME` | Storage directory | `~/.artifacty` |
720
+ | `ARTIFACTY_BACKUP_ON_UPDATE` | Snapshot existing stores before application-version changes; schema-migration snapshots are always required | `false` |
721
+ | `ARTIFACTY_UPGRADE_BACKUP_KEEP` | Automatic upgrade snapshots to retain; integer from 1 to 1000 | `3` |
582
722
  | `ARTIFACTY_URL` | Public URL override; otherwise CLI/MCP read the last running server URL | — |
583
723
  | `ARTIFACTY_HOST` | Bind host for the HTTP server | `127.0.0.1` |
584
724
  | `ARTIFACTY_PORT` | Bind port for the HTTP server | `8787` |
package/SECURITY.md CHANGED
@@ -2,18 +2,18 @@
2
2
 
3
3
  ## Supported Versions
4
4
 
5
- Artifacty is pre-1.0. Security fixes are provided for the latest published
6
- minor release. Upgrade to the latest `artifacty` version before reporting a
5
+ Artifacty is pre-1.0. The project provides security fixes for the latest
6
+ published minor release. Upgrade to the latest `artifacty` version before reporting a
7
7
  suspected issue.
8
8
 
9
9
  ## Reporting a Vulnerability
10
10
 
11
11
  Use GitHub private vulnerability reporting if it is enabled for this repository.
12
- If private reporting is unavailable, open a GitHub issue with a minimal summary
13
- and avoid including exploit details, secrets, private artifacts, or sensitive
12
+ If private reporting is unavailable, open a GitHub issue with a brief summary.
13
+ Do not include exploit details, secrets, private artifacts, or sensitive
14
14
  network information.
15
15
 
16
- Please include:
16
+ Include the following information:
17
17
 
18
18
  - Artifacty version and install method.
19
19
  - Node.js version and operating system.
@@ -23,27 +23,36 @@ Please include:
23
23
 
24
24
  ## Security Model
25
25
 
26
- Artifacty is local-first. The default HTTP server binds to `127.0.0.1`; binding
27
- outside loopback requires `ARTIFACTY_SHARE_MODE=lan|team` and an API token.
26
+ Artifacty is local-first. The HTTP server binds to `127.0.0.1` by default.
27
+ To bind outside loopback, set `ARTIFACTY_SHARE_MODE=lan|team` and an API token.
28
28
 
29
29
  The project treats artifact content as untrusted:
30
30
 
31
31
  - HTML, SVG, Mermaid, and React rendering use sandboxed frames and scoped CSP.
32
32
  - React execution is disabled unless `ARTIFACTY_ENABLE_REACT_RENDERER=true`.
33
- - Mutating browser routes reject non-local `Origin` headers.
33
+ - Mutating browser routes reject cross-origin writes. They allow loopback and
34
+ same-origin requests.
34
35
  - API token checks use constant-time digest comparison.
35
- - Artifact content is scanned for common API keys and private keys before
36
- storage unless explicitly allowed.
36
+ - Before storage, Artifacty scans content for common API keys and private keys.
37
+ The caller can explicitly allow content that contains these keys.
37
38
 
38
- Artifacty does not provide TLS termination, remote OAuth, multi-user access
39
- control, or a hosted sync service. Do not expose the server directly to the
40
- public internet.
39
+ Administrators can select public or private site access. Private mode requires
40
+ a personal account and limits artifact access to owners, administrators, and
41
+ users with direct or team grants. Grants permit reading, commenting, or editing.
42
+ Token scopes further restrict API and MCP actions.
43
+
44
+ Administrators can also restrict HTTP access by IP address or CIDR range.
45
+ Artifacty checks the TCP peer address and does not trust forwarding headers.
46
+ Public share links require explicit administrator opt-in in private mode.
47
+
48
+ Artifacty does not provide TLS termination, remote OAuth, or a hosted sync
49
+ service. Do not expose the server directly to the public internet.
41
50
 
42
51
  ## Supply Chain
43
52
 
44
- npm releases are published through GitHub Actions using npm Trusted Publishing
45
- OIDC. The release workflow runs lint, tests, and smoke checks on supported Node
46
- versions before publishing.
53
+ GitHub Actions publishes npm releases through npm Trusted Publishing OIDC.
54
+ Before publication, the release workflow runs lint, tests, and smoke checks on
55
+ supported Node versions.
47
56
 
48
57
  See [docs/threat-model.md](docs/threat-model.md) and
49
58
  [docs/network-sharing.md](docs/network-sharing.md) for operational guidance.