Skip to content

Repository files navigation

OCAP Web component

Go CoverageUI Coverage

OCAP Web serves and plays back Arma 3 mission recordings. It supports both legacy JSON recordings and chunked Protobuf format for efficient streaming of large recordings.

Installation

Pre-built binaries

Download the latest release from GitHub Releases:

PlatformArchive
Windows x64ocap-webserver-windows-amd64.zip
Linux x64ocap-webserver-linux-amd64.tar.gz
Linux ARM64ocap-webserver-linux-arm64.tar.gz

Each archive contains the binary and required assets (markers, ammo icons).

Build from source

Requires Go 1.26+ and Node.js 24+.

# Build the frontendcd ui && npm ci && npm run build &&cd ..
# Build the server (frontend is embedded into the binary)
go build -o ocap-webserver ./cmd/ocap-webserver
# Or build everything via Docker
docker build -t ocap-webserver .

For development setup and workflow details, see CONTRIBUTING.md.

Docker

Docker images are available for linux/amd64 and linux/arm64 architectures in two variants:

VariantTagDescription
Slim (default)latest, v1.2.3Web server only
Fullfull, v1.2.3-fullWeb server + integrated Map Manager (GDAL, tippecanoe, pmtiles)

The full variant includes all tools needed for the Map Manager — an admin page that processes Arma 3 map data (grad_meh exports) into PMTiles and MapLibre styles directly from the web UI. The server auto-detects the available tools at startup; no extra configuration is needed.

# Slim — just the web server
docker run --name ocap-web -d \
-p 5000:5000/tcp \
-e OCAP_SECRET="same-secret" \
-e OCAP_CONVERSION_ENABLED="true" \
-v ocap-records:/var/lib/ocap/data \
-v ocap-maps:/var/lib/ocap/maps \
-v ocap-database:/var/lib/ocap/db \
ghcr.io/ocap2/web:latest
# Full — web server with integrated Map Manager
docker run --name ocap-web -d \
-p 5000:5000/tcp \
-e OCAP_SECRET="same-secret" \
-e OCAP_CONVERSION_ENABLED="true" \
-v ocap-records:/var/lib/ocap/data \
-v ocap-maps:/var/lib/ocap/maps \
-v ocap-database:/var/lib/ocap/db \
ghcr.io/ocap2/web:full

Volumes

PathDescription
/var/lib/ocap/dataRecording storage (JSON and chunked formats)
/var/lib/ocap/mapsMap tiles (download here)
/var/lib/ocap/dbSQLite database

Pelican Panel

A Pelican Panel egg is provided for deploying OCAP2 Web as a managed server instance. Import egg-ocap2-web.json in the Pelican admin panel under Eggs → Import Egg.

The egg uses the project's Docker image (ghcr.io/ocap2/web) directly. Persistent data (database, recordings, maps) is stored under /home/container/ via Pelican's volume mount.

Configuration

The configuration file is called setting.json. All settings can also be set via environment variables with the OCAP_ prefix. Nested keys use underscores: auth.sessionTTLOCAP_AUTH_SESSIONTTL.

{
"listen": "127.0.0.1:5000",
"secret": "your-secret",
"logger": true,
"auth": {
"sessionTTL": "24h",
"adminSteamIds": ["76561198012345678"],
"steamApiKey": ""
},
"customize": {
"enabled": true,
"websiteURL": "https://example.com",
"websiteLogo": "https://example.com/logo.png"
},
"conversion": {
"enabled": true,
"interval": "5m"
},
"streaming": {
"enabled": true
},
"cors": {
"allowedOrigins": []
}
}

Server

SettingEnv VarDescriptionDefault
listenOCAP_LISTENServer address127.0.0.1:5000
prefixURLOCAP_PREFIXURLURL prefix for all routes""
secretOCAP_SECRETShared secret — authenticates record uploads and signs admin session JWTsrequired
loggerOCAP_LOGGEREnable request logging to STDOUTfalse

Paths

SettingEnv VarDescriptionDefault
dbOCAP_DBPath to SQLite databasedata.db
dataOCAP_DATAPath to recording storagedata
mapsOCAP_MAPSPath to map tilesmaps
markersOCAP_MARKERSPath to marker iconsassets/markers
ammoOCAP_AMMOPath to ammo iconsassets/ammo
fontsOCAP_FONTSPath to fontsassets/fonts
staticOCAP_STATICServe frontend from this directory instead of the embedded buildembedded

Docker note: The Docker image overrides path defaults to /var/lib/ocap/... and /usr/local/ocap/.... See Volumes for the Docker-specific paths.

Admin Authentication

Admin access uses Steam OpenID — no passwords. Admins authenticate via their Steam account and are authorized against an allowlist of Steam64 IDs.

SettingEnv VarDescriptionDefault
auth.sessionTTLOCAP_AUTH_SESSIONTTLHow long admin sessions last24h
auth.adminSteamIdsOCAP_AUTH_ADMINSTEAMIDSSteam64 IDs authorized for admin access (comma-separated, no brackets, in env var)[]
auth.steamApiKeyOCAP_AUTH_STEAMAPIKEYSteam Web API key for fetching display names and avatars (get one here)""

The Steam API key is optional. Without it, the admin badge shows the raw Steam64 ID. With it, the admin's Steam profile picture and display name are shown.

Customization

For a full theming guide with example themes and an AI prompt to generate your own, see Customization Docs.

SettingEnv VarDescriptionDefault
customize.enabledOCAP_CUSTOMIZE_ENABLEDEnable the customize endpointfalse
customize.websiteURLOCAP_CUSTOMIZE_WEBSITEURLLink on the logo to your website""
customize.websiteLogoOCAP_CUSTOMIZE_WEBSITELOGOURL to your website logo""
customize.websiteLogoSizeOCAP_CUSTOMIZE_WEBSITELOGOSIZELogo size32px
customize.disableKillCountOCAP_CUSTOMIZE_DISABLEKILLCOUNTHide kill counts in the UIfalse
customize.hideMapFiltersOCAP_CUSTOMIZE_HIDEMAPFILTERSHide the map filter dropdown on the recording listfalse
customize.headerTitleOCAP_CUSTOMIZE_HEADERTITLECustom header title""
customize.headerSubtitleOCAP_CUSTOMIZE_HEADERSUBTITLECustom header subtitle""
customize.cssOverridesOCAP_CUSTOMIZE_CSSOVERRIDESCSS variable overrides (JSON object, see below){}

CSS Overrides

Override any CSS custom property to theme the UI without rebuilding. In setting.json:

"cssOverrides": {
"--accent-primary": "#fcb00d",
"--bg-dark": "#1a2a1a",
"--text-on-accent": "#1a2a1a"
}

Via environment variable (for Docker), pass a JSON string:

OCAP_CUSTOMIZE_CSSOVERRIDES='{"--accent-primary":"#fcb00d","--bg-dark":"#1a2a1a","--text-on-accent":"#1a2a1a"}'

Common variables:

VariableDescriptionDefault
--accent-primaryPrimary accent color (buttons, links, highlights)#4A9EFF
--accent-primary-darkDarker variant for gradients#3585dd
--accent-dangerDanger/error color#FF4A4A
--accent-successSuccess color#2DD4A0
--accent-warningWarning color#FFB84A
--text-on-accentText color on accent-colored buttons#fff
--bg-darkMain background#0a0f14
--bg-surfaceCard/panel background#151e2b
--text-primaryPrimary text color#e5ebf1
--side-bluforBLUFOR faction color (map markers)#00a8ff
--side-opforOPFOR faction color#ff0000
--side-indIndependent faction color#00cc00
--side-civCivilian faction color#c900ff

Conversion

Large recordings can be automatically converted to chunked binary format for better performance.

SettingEnv VarDescriptionDefault
conversion.enabledOCAP_CONVERSION_ENABLEDEnable automatic background conversionfalse
conversion.intervalOCAP_CONVERSION_INTERVALHow often to check for pending conversions5m
conversion.batchSizeOCAP_CONVERSION_BATCHSIZEMax recordings to convert per interval1
conversion.chunkSizeOCAP_CONVERSION_CHUNKSIZEFrames per chunk (~5 min at 1 fps)300
conversion.retryFailedOCAP_CONVERSION_RETRYFAILEDRetry previously failed conversionsfalse

Streaming

Live mission data can be streamed to the server via WebSocket.

SettingEnv VarDescriptionDefault
streaming.enabledOCAP_STREAMING_ENABLEDEnable the WebSocket streaming endpointfalse
streaming.pingIntervalOCAP_STREAMING_PINGINTERVALInterval between WebSocket keepalive pings30s
streaming.pingTimeoutOCAP_STREAMING_PINGTIMEOUTTimeout waiting for pong response10s

CORS

All responses include CORS headers so external services and web apps can fetch from the API.

SettingEnv VarDescriptionDefault
cors.allowedOriginsOCAP_CORS_ALLOWEDORIGINSOrigins allowed to make cross-origin requests. Empty list permits all origins (*). Comma-separated in env var.[] (all origins)

When allowedOrigins is empty the server responds with Access-Control-Allow-Origin: *, which is appropriate for public read APIs. Restrict to specific origins if you want to limit which external sites can call admin endpoints:

"cors": {
"allowedOrigins": ["https://admin.example.com", "https://replay.example.com"]
}

Large Recording Support

Overview

Traditional JSON recordings load entirely into browser memory, which causes crashes with large missions (500MB+). The chunked streaming system solves this by:

  1. Converting recordings to binary format (Protobuf)
  2. Splitting into chunks (~5 minutes each)
  3. Loading only needed chunks during playback
  4. Caching chunks in browser storage (OPFS/IndexedDB)

Storage Formats

FormatExtensionUse CaseStreamingPerformance
JSON.gzLegacy, small recordingsNoBaseline
Protobuf.pbDefault chunked formatYesGood

Workflow

┌─────────────────────────────────────────────────────────────────┐
│ UPLOAD │
│ Mission ends → JSON.gz uploaded → Stored in data/ directory │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ CONVERSION │
│ Background worker (or CLI) converts to chunked binary format │
│ │
│ data/mission.gz → data/mission/ │
│ ├── manifest.pb (metadata + entities) │
│ └── chunks/ │
│ ├── 0000.pb (frames 0-299) │
│ ├── 0001.pb (frames 300-599) │
│ └── ... │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ PLAYBACK │
│ 1. Load manifest (entities, events, metadata) │
│ 2. Load chunks on-demand as playback progresses │
│ 3. Cache chunks in browser (OPFS) for future playback │
│ 4. Evict old chunks from memory (max 3 in RAM) │
└─────────────────────────────────────────────────────────────────┘

For detailed flowcharts of playback and conversion, see Streaming Architecture.

CLI Commands

Convert recordings manually using the CLI:

# Convert a single file
./ocap-webserver convert --input data/mission.json.gz
# Convert all pending recordings
./ocap-webserver convert --all
# Show conversion status of all recordings
./ocap-webserver convert --status
# Change storage format for an existing operation
./ocap-webserver convert --set-format protobuf --id 1

File Structure After Conversion

data/
├── mission_name.gz # Original JSON (preserved)
└── mission_name/ # Chunked binary format
├── manifest.pb # Metadata, entities, events
└── chunks/
├── 0000.pb # Frames 0-299
├── 0001.pb # Frames 300-599
└── ...

About

OCAP2 web server for Arma 3 after-action review and mission replay

Resources

Contributing

Stars

4 stars

Watchers

7 watching

Forks

Releases

Packages

Used by

Contributors

Languages