A persistent per-player image library for Rust signs, photo frames, banners, carvable pumpkins, neon signs, and artist canvases. Built on top of Sign Artist (Whispers88) — required dep.
Designed for servers where painted-sign art is a feature, not a one-wipe novelty: every artist on the server gets their own gallery that survives wipes, a public-gallery surface for sharing creations, and a commission/sell workflow with per-slot buyer access. Art is captured at apply time as both URL (for human reference) and raw PNG bytes (for reliable replay even after Discord-CDN expiry or /sil URL rot).
Painted sign in-world — applied from a saved slot via the browse panel.

My Library — your personal slot inventory. Save, rename, apply, publish, share.

Public Gallery — server-wide browseable art contributed by any artist.

Preview modal — full-size preview at the slot's native canvas dimensions.

Share modal — grant a specific buyer copy access (commission / sell workflow).

Shared With Me — slots other artists have shared with you.

Help & Information — in-game help reference.

- Install Sign Artist (required dep).
- On Linux hosts, install
libgdiplusBEFORE starting RustDedicated:sudo apt install libgdiplus
System.Drawinguses libgdiplus for apply-time PNG resizing; without it signs render white. The library is loaded at RustDedicated startup, not at plugin load — install before first launch. - Drop
SignArtSaver.csintooxide/plugins/(Carbon:carbon/plugins/). - The plugin auto-grants
signartsaver.useto thedefaultgroup on first boot (configurable viaAuto-grant signartsaver.use to default group on startup). - Paint a sign with
/sil <url>— the slot is auto-captured. Open the panel with/saveart.
That's it. Players can paint, save, browse, share, and re-apply across wipes.
| Component | Minimum | Notes |
|---|---|---|
| Rust dedicated server | Current build with Signage / PhotoFrame / CarvablePumpkin types |
Standard since the painted-sign system shipped. |
| Sign Artist | v1.4.x | Hard dep. Plugin self-unloads if absent. |
libgdiplus (Linux) |
Any recent | For System.Drawing PNG resizing. |
| Carbon framework | Any current | Oxide-compatible; both supported. |
| Permission | Auto-granted? | What it does |
|---|---|---|
signartsaver.use |
Yes, to default group (configurable) |
All player commands and the /saveart browse panel. |
signartsaver.admin |
No — grant manually | Bypass ownership checks; cross-library management; /saveart debug. |
Operators can disable the auto-grant in config and gate access through their own permission groups (members-only servers, etc.).
| Command | Description |
|---|---|
/saveart |
Open the browse panel (My Library tab). |
/saveart save [name] |
Save the painted sign you're aiming at (byte-mode; works on any painted sign, regardless of how it was painted). |
/saveart apply <slot> |
Arm the apply — look at YOUR sign and press USE within the timeout. |
/saveart rename <slot> <new name> |
Rename a slot (1–32 printable chars, no < > newlines). |
/saveart remove <slot> |
Two-step delete (run twice within the confirm window). |
/saveart wipe |
Two-step wipe of the entire library. |
/saveart publish <slot> |
Toggle a slot public/private. Adds to the Public Gallery. |
/saveart public [search] |
Open the Public Gallery tab (optional name/artist filter). |
/saveart share <slot> <name|steamid> |
Grant a specific buyer copy access (commission/sell workflow). |
/saveart unshare <slot> <name|steamid> |
Revoke a buyer's access. |
/saveart shared [slot] |
List buyers on a slot, or all your shared slots. |
/saveart shared-with-me |
List slots other artists have shared with you. |
/saveart list [page] |
Plain-chat list of your own slots. |
/saveart help |
Help text. |
| Command | Description |
|---|---|
/saveart admin <steamid> list |
List another player's slots. |
/saveart admin <steamid> apply <slot> |
Apply another player's slot to YOUR aimed sign. |
/saveart admin <steamid> rename <slot> <name> |
Rename a slot in another player's library (moderation). |
/saveart admin <steamid> remove <slot> |
Delete a slot from another player's library (moderation). |
/saveart admin <steamid> publish <slot> |
Publish a slot on another player's behalf (rare). |
/saveart admin <steamid> unpublish <slot> |
Take a slot down from the Public Gallery (moderation). |
/saveart debug |
Raycast diagnostic — prints what your crosshair is hitting. |
Admins bypass the per-player public-gallery cap.
Generated at oxide/config/SignArtSaver.json (Carbon: carbon/configs/SignArtSaver.json) on first load. Most defaults work for a typical server; adjust as needed.
The full default config is also shipped as SignArtSaver.example.json — copy it to your config dir for a fresh install, or diff it against your running config to see what you've customized. Tables below explain each key.
| Key | Default | Notes |
|---|---|---|
Slots per player (1-500) |
50 |
Per-player library cap. |
Max public slots per player... |
25 |
Cap on Public Gallery contribution. 0 = unlimited. Admins bypass. |
Max PNG bytes per saved slot... |
2097152 (2 MiB) |
Matches Rust's FileStorage upload cap. Raise it and slots above 2 MiB save but never apply — the plugin warns at load if you do. Lower freely for disk hygiene. |
Strip query string when hashing for dedupe |
true |
Treats ?token=... variants of the same URL as duplicates. |
Block URLs containing these substrings... |
["token=","auth=","Authorization","?key="] |
Substring blocklist for auto-capture. The cross-library URL-fallback path is also denied for IP literals and private TLDs (.local, .internal, .localhost, .lan, .home). |
Warn when saving Discord CDN URLs (they expire ~24h) |
true |
Saved anyway; the warning suggests imgur / GitHub raw / self-hosted for wipe-survival. |
| Key | Default | Notes |
|---|---|---|
Auto-capture URLs from OnImagePost |
true |
Whole UX assumes this is on. |
Require entity ownership for save |
true |
Recommended for any server with adversarial dynamics. |
Require entity ownership for apply |
true |
Admin bypasses. |
Strict entity-kind match on apply |
true |
Sign-to-sign, frame-to-frame, etc. Prevents stretched applies. |
Apply cooldown per player (seconds) |
2.0 |
Anti-spam. |
Pending-apply timeout (seconds) |
30.0 |
Armed slot expires if no USE key. |
| Key | Default | Notes |
|---|---|---|
CUI rows per page (4-16) |
8 |
Browse panel page size. |
Truncate URL display length (16-120) |
40 |
Long URLs trimmed with …. |
Confirm-delete window (seconds, 1-30) |
5.0 |
Two-step delete confirm. |
Wipe-confirm window (seconds, 5-60) |
10.0 |
Two-step wipe confirm. |
| Key | Default | Notes |
|---|---|---|
Aim distance for /saveart save and /saveart apply raycasts (meters; 1-30) |
8.0 |
How far the chat / UI raycast reaches when looking for a sign in your aim. |
| Key | Default | Notes |
|---|---|---|
Self-heal: re-apply cached bytes... |
true |
Survives the "sign blanks server-side" bug. |
Self-heal: max repairs per applied-entity in 24h |
3 |
Repair-cap before flagging an entity. |
Self-heal: clone bytes into the applier's own library... |
true |
Lets buyer art survive even if the original artist deletes their slot. |
Self-heal diagnostic: verbose per-entity logging |
false |
One log line per scanned slot. Off in steady-state. |
Self-heal diagnostic: periodic scan interval in minutes |
0 |
0 = off. 1-60 runs the verify pass for every online player on a recurring timer. Diagnostic — enable when bracketing a trigger. |
| Key | Default | Notes |
|---|---|---|
Auto-grant signartsaver.use to default group on startup |
true |
Fresh installs "just work". Operators who gate features by group can flip this false and grant manually. |
⚠ Note on upgrades: Oxide deserializes config by the EXACT description string. If a future release changes a description, the value at the old key is silently reset to the new default. Re-check non-default settings after every plugin update.
/saveart publish <slot> adds a byte-mode slot to a server-wide gallery. Other players can browse via /saveart public [search], preview, and apply public art to their own signs. Byte-mode only — URL-only slots can't be published (the original-owner's signartist.url permission isn't transferable).
- Per-player cap (config:
Max public slots per player, default 25) limits how many slots any one player can contribute to the gallery. Admins bypass. - Admins can take down a slot with
/saveart admin <steamid> unpublish <slot>or rename it with... rename <slot> <new name>. - Slot names are sanitized at the save source — angle brackets, control chars, and rich-text tags are stripped before the name reaches anyone else's chat.
Different from publishing: /saveart share <slot> <name|steamid> gives a SPECIFIC buyer copy access. Buyer sees the slot in their "Shared with me" tab, can preview, can apply. Built for commission/sell workflows.
- Per-(artist, recipient) cooldown (5 min) prevents share-spam loops.
- Cross-library URL fallback is refused — the buyer's server will never fetch the artist's URL on their behalf (no SSRF).
Painted signs occasionally lose their image server-side (decay near limit, engine glitch, vanilla painter wipe, sometimes a Sign Artist Store failure). Self-heal scans every tracked sign on player connect (and optionally on a periodic timer) and re-applies the cached bytes if the entity's CRC has been zeroed.
The 24-hour repair cap stops runaway loops if the trigger keeps firing. Ownership is re-checked at heal time — if a base has changed hands since the original applier touched the sign, the tracking record is dropped instead of repainting someone else's canvas.
For diagnosing a recurring trigger, set Self-heal diagnostic: periodic scan interval in minutes to a small value (e.g. 1) and watch the server log. The trigger window is logged on every detected break.
Every player-facing string — usage hints, error responses, permission denials, the help-body text, share/unshare overviews, the /saveart debug raycast output, the whole confirmation surface — lives in oxide/lang/<locale>/SignArtSaver.json (Carbon: carbon/lang/<locale>/). English defaults are auto-generated on first load; community translations can be dropped in by adding a new <locale>/SignArtSaver.json file with the same keys. 115 keys total.
Translators: string.Format placeholders ({0}, {1}, etc.) for parameterized templates. If a translation has a placeholder/arg-count mismatch, the helper falls back to the English default with a PrintWarning so the operator can spot the bad translation.
Server-operator log messages (Puts, PrintWarning, PrintError) stay English by design — the server log is operator-only.
- Sign Artist version pin — audited against 1.4.x. Versions outside that range print a startup warning; auto-capture and replay may silently miss if the hook signature or API_Skin* arguments change.
- No support for drawable windows / paintable reactive targets / spinning wheels — those use
PaintedItemStorageEntitywhich the plugin doesn't currently target (the uMod policy againstSystem.Reflectionmakes the canonical write path unavailable). Standard signs, photo frames, banners, hanging signs, neon signs, artist canvases, carvable pumpkins, and all DLC art frames are fully supported. - Discord CDN URLs — Discord rotates CDN URLs ~24 hours. A slot's URL goes stale, but the cached bytes survive wipes and are preferred on apply. Players save imgur / GitHub raw / their own host for URL durability.
- Auto-capture timing — Sign Artist's
OnImagePostfires synchronously; the actual download finishes a few seconds later. SignArtSaver defers the byte capture via a 5-second retry-poll. On slow CDNs the slot is saved URL-only and bytes can be back-filled by re-running/saveart savewhile aimed at the painted sign.
The plugin registers ~25 signartsaver.ui.* console commands. These are internal — they're invoked by the CUI buttons in the browse panel. Not a public API; don't script against them.
- Hard dep on Sign Artist by Whispers88 — the URL-paint workhorse.
- Author: Xphox.
- License: MIT — see
LICENSE.
GitHub: https://github.com/xphox2/SignArtSaver
Please file bugs with: Rust build number, Carbon/Oxide version, Sign Artist version, plugin version, and the server log lines tagged [SignArtSaver] from around the issue.