shire Docs
Docs/Plugin API/Permissions & security

Permissions and Security

Model

A plugin declares capabilities in its manifest. The Host exposes a fixed set of operations; each operation maps to a permission:

Permission Operations
player.read player.getPosition
world.read world.getScene
entities.read entities.list
map.read map.getMarkers
teleport.write teleport.to
ui.create ui.setLayout, ui.update
notifications.show notifications.show
storage.read storage.get, storage.keys
storage.write storage.set, storage.remove
events.subscribe events.subscribe, events.unsubscribe
game.invoke game.functions, game.invoke

api.info, plugin.hello, plugin.ping and logging.log require no permission; logging is rate-limited and capped.

The user approves permissions per plugin in the Plugin Manager. Approval is not a one-time install flag: the Host checks the granted set on every call. A revoked permission fails immediately with -32000.

Isolation

  • Separate process. A plugin is launched as a child process. No plugin DLL is loaded into the Shire process. A crash or hang closes that plugin's pipe and cannot directly corrupt the Core.
  • Job object. Each child is assigned to a job with a 256 MiB process-memory limit and KILL_ON_JOB_CLOSE; stopping the plugin terminates the job.
  • Restricted token. The Host tries CreateRestrictedToken with DISABLE_MAX_PRIVILEGE and CreateProcessAsUserW. When that is available the child does not retain the game's administrative privileges. If the environment cannot create the token/process, the Host falls back to CreateProcessW and records the fallback in the plugin log; the capability boundary remains, but the OS token is inherited.
  • No native handles. The JSON-RPC contract contains no game pointers, offsets, HMODULEs, file handles or ImGui context. Entity ids are opaque hex strings; the Host resolves them internally.
  • Package isolation. .shiremod extraction rejects absolute paths, .., drive colons and deflated entries, and caps entry/total size. A package is extracted to a staging directory before it replaces an existing plugin.

Input validation

Every handler validates type, length, range and finite numbers before touching the Core. UI layouts are capped (64 components, 16 KiB text). Storage keys are capped at 128 bytes and total storage at 256 KiB. Notifications are capped. Unknown methods return -32601; unknown permissions are rejected by manifest validation.

Rate limits and queues

  • 300 calls/second per plugin, including a burst; excess is -32001.
  • Inbound queue: 2,048 lines; a flooding connection is closed.
  • Outbound event queue: 256 lines per connection; oldest dropped.
  • Pipe writes have a timeout; a plugin that stops reading is disconnected instead of blocking the Host forever.
  • The Host processes at most 32 inbound lines per frame and does not call the Core when no plugin is subscribed to an event.

What is not guaranteed

No client-side system can guarantee secrecy against a user who controls the machine. A determined user can inspect the plugin process, the payload, the pipe traffic or the Windows APIs. The design minimizes unnecessary exposure:

  • the SDKs never contain offsets or private code;
  • the Host sends only public copies;
  • the plugin process is not in the Core address space;
  • approvals are explicit and per-call.

A plugin process is still a normal OS process. It can open files and sockets that the OS permits; the permission system governs the Shire API, not the whole operating system. If a future release needs stronger network/filesystem isolation, an AppContainer or a separate low-privilege service is the next step.

Never put administrative secrets (Cloudflare tokens, Discord bot tokens, license signing keys, client HMAC secrets) in a manifest, package, SDK or log. The SDKs contain none, and the Host never sends them over the pipe.

Shire Plugin API 1.0 · Written in English. Documentation home · API status · Changelog

Independent project. Not affiliated with HoYoverse. Never put secrets in a plugin, manifest or package.