CHIM Plugin Guide

A practical guide for authors who want to build, package, list, and support a CHIM server plugin for Skyrim. It covers when you need a plugin at all, how to write a first one, how Plugin Manager finds and installs it, and how to describe it so players can install and report problems.

Start Here

A CHIM server plugin is PHP code that runs inside HerikaServer from its own folder under HerikaServer/ext/<name>. It extends CHIM without editing the core server. Typical uses are:

  • Mod awareness: tell AI characters about the state of another Skyrim mod, such as survival needs or cleanliness.
  • Context and prompts: add short, relevant lines to the prompt CHIM builds for a character.
  • Actions: give AI characters something new they can do, together with game-side code that carries it out.
  • Dashboards and external integrations: a plugin page in the CHIM web interface, a background worker, or a connection to another service such as Twitch chat.

This guide is for CHIM with Skyrim and HerikaServer. Stobe and Dialectic have their own servers and are not covered here.

Related references

Maintained example plugins

  • CHIM-Custom: optional Skyrim mod state, a native game-side component, PHP context hooks, migrations, and release scripts.
  • CHIM-Twitch-Bot: a plugin page, a background service, and an external integration.
  • CHIM-MindMap: a visual tool built on top of CHIM's stored memory data.

Choose An Approach

Many CHIM integrations do not need a server plugin. Pick the smallest route that does the job.

Data files in Data/CHIM

Ship CSV files inside your Skyrim mod. CHIM uploads them to the server automatically when the game starts. See Custom CHIM Data Mods for the format.

  • _bios.csv: NPC biographies.
  • _oghma.csv: lore and knowledge entries.
  • _dynamicoghma.csv: knowledge that changes as quests progress.
  • _descriptions.csv: descriptions for items and other content.
  • _actions.csv: action definitions.

An _actions.csv row defines what the AI can call. It does not, by itself, make anything happen in the game. Gameplay still needs code that performs the action, unless the row uses a simple server-side script proxy.

A PHP server plugin

Use a server plugin when you need to change how HerikaServer behaves: adding prompt context, reacting to requests, storing your own data, serving a plugin page, or talking to another service.

A Papyrus or SKSE bridge

Use game-side code when something must change inside Skyrim, such as moving an NPC, giving an item, or reading another mod's state. A Papyrus script or SKSE plugin carries out the command and reports the result back to CHIM. Many integrations combine this with an actions CSV and, optionally, a server plugin.

Your First Server Plugin

Set up

  • Create a separate Git repository for your plugin. Do not develop inside a live HerikaServer/ext folder or inside a HerikaServer checkout.
  • Test on a disposable or backed-up CHIM server, not the server you play on.
  • Start with only the files you need.
MyPlugin/
  README.md          what it does, requirements, settings, limits
  context_pre.php    a hook file (only the hooks you use)
  index.php          optional plugin page in the CHIM web interface
  migrations/        optional ordered .sql files
    001_initial.sql

Supported hook files

HerikaServer loads a hook file by name from every folder under ext. You only create the hook files your plugin needs. These are the main request hooks:

Main request hooks, in the order main.php loads them
File When it runs Typical use

globals.php

Early, after core libraries load.

Shared definitions, settings, and registrations.

preprocessing.php

Near the start of request handling.

Normalize or prepare incoming data.

prerequest.php

Before non-LLM event handling.

Inspect the request before most events are processed.

context_pre.php

Before the system prompt is assembled.

Add context or register prompt injections.

context.php

After the system prompt is built.

Adjust context once the main structure exists.

prepostrequest.php, postrequest.php

After the response, around core post-request processing.

Save data or queue follow-up work.

Other hook files exist for prompt templates, history building, and JSON responses: prompts.php, dialogue_prompt.php, context_building.php, and json_response_custom.php. See Creating Plugins for each caller.

How hooks are called

  • Read the caller. The code that loads a hook decides what data is ready and in what order. Check the matching file in the HerikaServer version you target.
  • Variables: hook files are included from inside a loader function, not the global scope. $gameRequest is available; read and write anything else through $GLOBALS.
  • Once per request: each hook file is included once per request.
  • Not every request reaches every hook. Many game events finish early, before any context hook runs. Some web pages and endpoints load only globals.php. Do not rely on a later hook having run.

Write it carefully

  • Prefix everything. Functions, constants, globals, and tables share one server with other plugins. Use a unique prefix such as myplugin_, or a namespace.
  • Handle missing optional pieces. If a Skyrim mod, service, setting, or older server function is absent, return quietly instead of raising warnings.
  • Keep prompt text short. Add one or two relevant lines, only when they apply. Every line costs context for every request.
  • Keep heavy work out of dialogue. Do not call slow services or extra LLM requests while the player waits. Queue the work and process it in postrequest.php or a background worker.
  • Log, do not echo. Responses are streamed back to the game. Printing text from a hook can corrupt them. Write diagnostics to a log instead, and never log secrets.
  • Use the plugins schema for your tables, with ordered, idempotent files in migrations. Never edit a migration after release; add a new one.

Plan for other mods, saves, and costs

  • Check what alternatives really support. A replacement or stub for a dependency may implement most functions but not all. Detect each feature you call, use a safe default when it is missing, and never let an unsupported check silently stop the plugin's work. List the exact dependency versions you tested.
  • Share state carefully. If other mods also change the same value, say what your plugin writes, any limits or cooldowns it applies, and what happens when it is disabled.
  • Make every off switch real. Disabling a feature should skip its work and its prompt text, not only hide it.
  • Restore temporary changes. If a plugin overrides settings for a scene or a group of NPCs, restore them when the scene ends, on errors, after a save is loaded, and when a dependency is missing. Test with crowds and several NPCs.
  • Explain what persists. Uninstalling stops future processing but does not undo changes already made to saves or stored data. Describe what happens when players switch or load saves and upgrade, and ask them to back up first. Do not tell players to delete database tables by hand.
  • Be clear about extra model calls. State whether a feature makes additional LLM calls, which backend it uses, roughly what it costs, whether it is optional, and what happens if the call fails.
  • Do not promise more than the game does. Prompt text changes what characters say, not what happens in Skyrim. Real game effects need a documented API or game-side code in the versions you target.
  • Make builds reproducible. Build scripts should not contain your own drive paths or tool locations. Make tools configurable, keep source separate from release files, and test the released files rather than your working copy.

Example: add one line of context

This context_pre.php registers a short line at the bottom of the prompt. It does nothing on servers without the prompt injection API, and nothing when there is no state to report.

<?php
// ext/MyPlugin/context_pre.php

if (!function_exists('chimRegisterPromptInjection')) {
    return;
}

if (!function_exists('myplugin_weather_line')) {
    function myplugin_weather_line(string $slot, array $context): string
    {
        $weather = $GLOBALS['MYPLUGIN_WEATHER'] ?? '';
        if (!is_string($weather) || $weather === '') {
            return '';
        }
        return 'Current weather: ' . $weather . '.';
    }
}

chimRegisterPromptInjection('prompt_bottom', 'myplugin.weather', 'myplugin_weather_line', 100);

chimRegisterPromptInjection() supports the character_bottom and prompt_bottom slots. Lower priority values render first, and empty results are skipped.

Validate before you test in game

Lint every PHP file you changed:

php -l context_pre.php

# every PHP file in the plugin folder
find . -name '*.php' -print0 | xargs -0 -n1 php -l

Then test only what you changed, on a disposable server:

  • Install the plugin and confirm the server still responds normally.
  • Trigger the requests your hooks handle and read the logs for PHP warnings.
  • Test with each optional dependency missing.
  • Test alongside at least one other plugin.
  • Test a clean install and an upgrade if you have migrations.
  • Test with the real game. A copied server and simulated requests catch errors, but not dialogue quality, long sessions, or save and load behavior.

Package, List, And Update

Plugin Manager finds plugins on GitHub. You do not need to submit a catalog entry, open a pull request, or write a manifest.

Get listed

  1. Make the repository public. It must not be a fork and must not be archived.
  2. Add the chim-plugin topic in the repository's GitHub About settings. The listing shows the repository owner, name, and description.
  3. Publish a stable GitHub release. Drafts and prereleases are ignored.
  4. Attach the server files as a release asset named exactly chim-plugin.tar.gz (or chim-plugin.tar).

Archive layout

Put the server files at the root of the archive. Do not wrap them in a folder.

chim-plugin.tar.gz
  context_pre.php
  index.php          (optional plugin page)
  lib/...
  migrations/...     (optional)

For example, from inside a folder that holds only the files to ship (no .git, logs, or development files):

tar -czf ../chim-plugin.tar.gz .
  • Only regular files and folders with relative paths are accepted. Links, special files, and .. paths are rejected.
  • Other release assets, such as a Skyrim mod archive, are never installed.
  • If index.php or index.html is at the archive root, Plugin Manager links to it as the plugin page.

Versions and folder names

  • Version: the release tag is the version players see. Use letters, digits, ., _, +, or -. An update is offered whenever the latest tag differs from the installed tag, so semantic versioning is not required.
  • Folder: the plugin installs into ext/<repository name>. The repository name must be 1–64 letters, digits, _, ., or -, starting with a letter or digit.
  • Collisions: install and update are refused if that folder already belongs to a different repository or an unknown source. Delete the existing plugin first to replace it on purpose.
  • Generated manifest: Plugin Manager writes its own ext/<name>/manifest.json at install. A manifest.json inside your package is optional and cannot change the name, source, version, channels, branding, or plugin page.

Updates

A Plugin Manager update replaces the whole plugin folder. Nothing inside it is preserved. Keep user settings and data in the database, or outside the plugin folder, if they must survive updates.

Refresh and installability

  • Results are cached for six hours. Refresh Plugins forces a new check, with a back-off after GitHub errors.
  • If GitHub is unavailable or the hourly request limit is reached, the last known list stays visible and Plugin Manager shows which part is stale.
  • Only the 100 most recently updated repositories with the topic are checked.
  • A repository with the topic but no qualifying release is still listed but not installable. Plugin Manager shows the reason, such as a missing chim-plugin.tar.gz asset or an invalid repository name.

Plugins with an existing manifest

Plugins already published with a root manifest.json and channels keep working. Once a release includes chim-plugin.tar.gz, that asset replaces the old release channel and installed copies switch over in place. Branch channels such as Dev remain available. The HerikaServer documentation describes the legacy manifest fields.

Packages bundled with a Skyrim mod

A Skyrim mod can carry its matching server plugin so CHIM installs it on the connected server when the game starts. This uses a different format: a schema 4 ZIP or .dwpkg package with an outer manifest.json, a checksums.sha256 file, and the server files under server/. Its updater can preserve listed mutable_paths.

Do not mix the two formats, and choose one install and update route for each plugin. Follow the package contract and the CHIM-Custom packaging scripts.

Game-side companion files

If your plugin needs a Skyrim mod, ESP, Papyrus scripts, or SKSE DLL, players install that separately with their mod manager. Never put game DLLs or other game files in the server package.

Choosing Downloads

Players get confused when a release page has several files and no instructions. Offer one route for the server plugin, and add a game-side download only if the plugin really needs one.

Common release files and how each one is used
File Installed by Notes

chim-plugin.tar.gz

Plugin Manager, from the GitHub release.

Server files only. See Archive layout.

.dwpkg package

CHIM, which sends it to the server when a save loads.

Players do not unpack it. It goes inside the mod-manager archive under Data/CHIM/server-plugins/. See bundled packages.

Mod-manager ZIP

Mod Organizer 2, Vortex, or another mod manager.

Holds game-side files, a bundled .dwpkg, or both. Its folder structure must match the route you chose.

Source code archive

Nobody. GitHub adds it automatically.

Not an installer. Tell players to ignore it.

  • Server plugin with no game files: list it through Plugin Manager, or bundle the .dwpkg in a mod-manager ZIP. A ZIP used only to carry the package needs no ESP, Papyrus scripts, or plugin slot.
  • Plugin with a game-side mod: say clearly that players need both parts, and link each one directly.
  • Optional extras: label them optional, and say what works without them.
  • Mod manager metadata: Mod Organizer 2 may warn when an archive has no meta.ini. Including one with your version is helpful, but it only adds metadata. It does not make a wrongly structured archive work.
  • Mod managers: state which mod managers you actually tested.

Install and verify

  1. Install and enable the mod-manager ZIP, if the plugin uses one.
  2. Install the server plugin from Plugin Manager, if that is the chosen route.
  3. Start Skyrim with CHIM enabled and load a save. Bundled packages are sent to the server at this point.
  4. Check the version. Plugin Manager shows the installed release tag. Bundled packages appear on the server's Server Plugins page under Automatic Game Plugin Sync; use Refresh Status.
  5. Open the plugin page, if there is one, and check its settings.

When something does not work

  • Not shown on the server at all: the package was not installed or synchronized. Check that the mod is enabled, a save was loaded with CHIM connected, and the archive structure matches the author's instructions.
  • Installed, but the plugin page shows an error: the files arrived, but the page could not find what it needs, such as an active playthrough or save. Load a save in game, then reopen the page.
  • Mod manager warnings: read them. They are not always harmless, and not always fatal.
  • Reporting: copy the exact error text, plugin version, install route, and mod manager, and attach the relevant logs. Do not guess at a cause without them. See the bug report template.

Actions

A working action has three parts:

  1. Define: an action row tells the AI the action's name, meaning, scope, and parameters. Ship it in an _actions.csv file.
  2. Execute: game-side code, such as a Papyrus bridge, performs the action. Simple server-only actions can use a script proxy instead.
  3. Report the actual result: the bridge reports what really happened back to CHIM, commonly through a funcret message. Report failure as failure, so the AI does not describe something that did not happen.

Read Action Editor for the CSV columns, dispatch types, and script proxy limits. The CHIM-NFF example shows a complete action row and Papyrus bridge.

Interact extensions (unstable only)

Work to let plugins add their own Interact actions has been merged into the unstable branches of HerikaServer and CHIM. It lets a server plugin declare actions in interact_actions.php, and a Skyrim mod register Papyrus-handled actions.

  • It is not in the production release. Do not ship a plugin that depends on it to players.
  • Papyrus-handled actions need a matching pair of unstable CHIM and HerikaServer builds.
  • The API may still change. Read the pull requests and their documentation before experimenting.

Sharing Your Plugin

Use this template for your README or release page so players know what they are installing.

# Plugin name

One-sentence description.

## What it does
- What changes in game or in the CHIM web interface.

## Requirements
- CHIM version:
- HerikaServer version:
- Required dependencies (Skyrim mods, SKSE plugins, services), with tested versions:
- Optional dependencies, and what works without them:
- Mod managers tested:

## Download and source
- Server plugin route (pick one): Plugin Manager (chim-plugin.tar.gz)
  or .dwpkg bundled in the mod-manager ZIP
- Mod-manager ZIP (if any): direct link, and what is inside it
- Files to ignore: source code archives
- Source: link

## Install
1. Install and enable the mod-manager ZIP (if any).
2. Install the plugin from Plugin Manager (if that is the route).
3. Load a save with CHIM enabled, then check the plugin version.

## Settings
- Where to find them, what each one does, and how to turn features off.

## Extra model calls
- Which features call an LLM, which backend, rough cost, and fallback.

## Update and uninstall
- Updates replace the plugin folder. Note anything that is lost.
- How to remove the plugin and any game files.
- What stays in saves or stored data after uninstalling.
- How to roll back, and what to back up first.

## Known limits
- What it does not do, and known conflicts.

## Testing
- Lint: which files were checked.
- Install: clean install / upgrade tested on CHIM and HerikaServer versions.
- Independent testing: what someone other than the author checked.
- In game: what was actually tested in Skyrim, and what was not.

## Bug reports
- Where to report, and what to include (see below).

Be specific about testing. Passing php -l, installing on a server, and working in game are three different claims. Only state the ones you checked.

Bug report template

Plugin version:
CHIM version:
HerikaServer version:
Other relevant mods:
Install route and mod manager:

Steps to reproduce:
1.
2.

Expected result:
Actual result:

Relevant logs (remove API keys, tokens, and personal paths):

See Log Files for where CHIM and server logs are stored.

Trust And Listings

The chim-plugin topic is a community listing, not a review or an endorsement by Dwemer Dynamics. Installing a plugin runs its PHP code and database migrations with full server privileges. Players should install plugins only from authors they trust, and authors should keep their source public and their changes clearly described.