Slotting 1.0.0

Install and operate

The install is one server process: the JSON API, the components, the admin page and these pages. Its state (layouts, waves, reservations, scans) lives in a store: Redis for production, or memory for a trial.

Install#

The executable#

The download has one archive per platform (Linux x64 and arm64, macOS, Windows), each with a single executable that needs no Node.js or other runtime. Unpack it and start it:

bash
tar -xzf slotting-1.0.0-linux-x64.tar.gz
cd slotting-1.0.0-linux-x64
REDIS_URL=redis://redis.internal:6379 ./slotting

It listens on http://127.0.0.1:10083 (set HOST and PORT to change that) and logs to standard output. Open http://127.0.0.1:10083/ for the admin page and /docs/ for these pages. Run it under your service manager (systemd, Windows service wrapper, launchd) so it restarts after a failure or a reboot. It writes nothing to disk.

On Linux the executable needs glibc 2.28 or later, libstdc++ and libatomic, which minimal installs and slim images often lack (the program then exits at once with "error while loading shared libraries: libatomic.so.1"). Install it first: sudo apt-get install -y libatomic1 on Debian and Ubuntu, sudo dnf install -y libatomic on RHEL and Amazon Linux. The archive's Dockerfile installs it.

Docker#

The Linux archives contain a Dockerfile that builds an image from the executable:

bash
cd slotting-1.0.0-linux-x64
docker build -t slotting:1.0.0 .
docker run -d --name slotting -p 127.0.0.1:10083:10083 \
    -e REDIS_URL=redis://redis.internal:6379 \
    -e LICENSE_FILE=/run/secrets/slotting-license -v /etc/slotting/license.txt:/run/secrets/slotting-license:ro \
    -e SLOTTING_INSTANCE_ID=slotting-1 \
    slotting:1.0.0

In the image the server listens on 0.0.0.0:10083. Publish the port only on a private interface or network (above: 127.0.0.1 of the host), never on a public one. The container is stateless and works with --read-only. Set SLOTTING_INSTANCE_ID, because a container's hostname changes every time it is recreated (License).

Check the install#

bash
curl -s http://127.0.0.1:10083/healthz     # {"status":"ok","store":"redis","storeOk":true}
curl -s http://127.0.0.1:10083/v1/info     # version, license, photo import

/healthz answers 503 with "status": "degraded" and "storeOk": false while Redis can't be reached; use it for your load balancer's and monitoring's health check. The Status page shows the same in a browser.

Configuration#

Everything is configured with environment variables. All are optional: without any, the install runs on 127.0.0.1:10083 with the memory store and the evaluation license.

Variable Default
PORT 10083 The HTTP port
HOST 127.0.0.1 The interface to listen on. 0.0.0.0 for all interfaces (only on a private network)
LICENSE_KEY Your license key (one line starting with SL2.)
LICENSE_FILE Or: the path of a file that holds it, for example a mounted secret. See License
LOG_LEVEL info debug, info, warn or error
SLOTTING_INSTANCE_ID derived from the hostname This installation's id, shown on the Status page. 1 to 128 of A-Z a-z 0-9 . _ : -
SLOTTING_STORE redis when a Redis setting is set, else memory memory or redis
REDIS_URL One Redis server: redis://host:6379, rediss://host:6380 for TLS. User and password may be in the URL (redis://user:password@host:6379/0); the path may only be a database number
REDIS_PASSWORD The password, if it isn't in the URL (also used for cluster and sentinel nodes)
REDIS_TLS_CA_FILE the system's CAs A PEM file with the CA certificate of your Redis (private CA). Needs rediss:// URLs
REDIS_CLUSTER_NODES Redis Cluster: a comma-separated list of node URLs (rediss://n1:6379,rediss://n2:6379). Instead of REDIS_URL
REDIS_SENTINELS Redis Sentinel: a comma-separated list of sentinels (host:26379 or URLs). Instead of REDIS_URL
REDIS_SENTINEL_NAME The name of the master the sentinels monitor. Required with REDIS_SENTINELS
REDIS_KEY_PREFIX slotting: Prefix of every key (1 to 64 of A-Z a-z 0-9 . _ : -). Give separate installs on one Redis different prefixes
REDIS_LOCK_WAIT_MS 5000 How long a request waits for a layout that another request is changing, 100 to 60000, before it answers 503 store_busy. REDIS_LOCK_TIMEOUT_MS is its old, deprecated name
REDIS_LOCK_TTL_MS 15000 How long a layout's lock lives if the instance holding it stops, 1000 to 600000. The holder keeps extending it while it works; keep it well above the longest wave planning
REDIS_COMMAND_TIMEOUT_MS 2000 How long a request waits for Redis to answer, 100 to 60000, before it answers 503 store_unavailable (a hung or overloaded Redis)
SLOTTING_WAVE_RETENTION_HOURS 72 Closed waves older than this are deleted when the next wave is created on their layout
SLOTTING_CORS_ORIGINS none Comma-separated origins (https://wms.example.internal) whose pages may call /api and /v1/info from the browser. Needed only when the components run on another origin than the install (Embedding)
SLOTTING_ALLOWED_HOSTS any Comma-separated host names, without port (slotting.internal.example,10.0.4.12), that the install answers to. Requests with any other Host header get 421 host_not_allowed. Protects against DNS rebinding; recommended in production. Include the names your health checks use (127.0.0.1, localhost)
SLOTTING_COMPACT_MAX_WAIT 3 Strict compactness: after how many releases an order that waits for a compact group of bins takes the best one it can get, 0 to 1000 (why). Read when a wave is created
SLOTTING_LIGHTS none Put-to-light: none, http, mqtt or simulator. The driver's settings (SLOTTING_LIGHTS_URL, SLOTTING_LIGHTS_TOKEN, SLOTTING_LIGHTS_MQTT_URL, SLOTTING_LIGHTS_MQTT_USERNAME, SLOTTING_LIGHTS_MQTT_PASSWORD, SLOTTING_LIGHTS_MQTT_CA_FILE, SLOTTING_LIGHTS_MQTT_PREFIX, SLOTTING_LIGHTS_RESYNC_MS) are in Put-to-light
SLOTTING_WEBHOOK_URLS Webhooks: the URLs that receive the events, with SLOTTING_WEBHOOK_SECRET (required), SLOTTING_WEBHOOK_EVENTS, SLOTTING_WEBHOOK_TIMEOUT_MS, SLOTTING_WEBHOOK_MAX_ATTEMPTS (default 20, about 8 hours of retries), SLOTTING_WEBHOOK_POLL_MS and SLOTTING_OUTBOX_MAX_PER_LAYOUT (default 100000). Without URLs no events are kept. See Webhooks
SLOTTING_AI_PROVIDER Photo import: anthropic, bedrock, openai, azure-openai, google or openai-compatible (which settings each needs)
SLOTTING_AI_MODEL claude-opus-5-5 for anthropic The vision model. Required for the other providers (for Azure: the deployment)
SLOTTING_AI_API_KEY The provider's API key. Falls back to ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY or AZURE_OPENAI_API_KEY
SLOTTING_AI_BASE_URL the provider's The endpoint: required for openai-compatible (http://ollama.internal:11434/v1), optional for the others (a proxy or gateway)
SLOTTING_AI_TIMEOUT_MS 60000 Time limit of one photo request to the provider, 1000 to 600000
AZURE_OPENAI_ENDPOINT Azure OpenAI: https://<resource>.openai.azure.com
AZURE_OPENAI_DEPLOYMENT Azure OpenAI: the deployment of a vision model
AZURE_OPENAI_API_VERSION 2024-10-21 Azure OpenAI: the API version
AWS_REGION the SDK's default Bedrock: the region. Credentials come from the default AWS chain (environment, shared config, instance or task role)

Settings that can't work (an unknown SLOTTING_STORE, a malformed Redis URL, two Redis modes at once, an unreadable REDIS_TLS_CA_FILE or SLOTTING_LIGHTS_MQTT_CA_FILE, a webhook URL without a secret) stop the install at startup with a message. Passwords and keys never appear in the log or in any response.

Showcase and demo limits#

For a public demo or a shared evaluation install, a few settings cap what visitors can create. They are all off unless set, and a production install doesn't need them. A request beyond a cap answers 409 limit_reached, with the setting in details.limit and its value in details.max. A value that can't work stops the install at startup.

Variable Default
SLOTTING_MAX_LAYOUTS no limit The most layouts on the install, 1 to 100000 (the demo layout counts). Creating one more answers 409 limit_reached
SLOTTING_MAX_WAVES no limit The most waves stored, on all layouts together, 1 to 1000000. Closed waves count until they are deleted (DELETE /api/waves/{id}, SLOTTING_WAVE_RETENTION_HOURS or SLOTTING_WAVE_MAX_AGE_HOURS); with this cap set, every layout's closed waves past the retention are deleted before a wave create counts
SLOTTING_MAX_ORDERS_PER_WAVE 5000 The most orders in one wave or one POST /api/plan, 1 to 5000 (it can only lower the API's limit)
SLOTTING_WAVE_MAX_AGE_HOURS never Hours, above 0: when a wave or a layout is created, every wave created longer ago (active, complete or closed, on any layout) is closed and deleted
SLOTTING_LAYOUT_RETENTION_HOURS never Hours, above 0: when a layout is created, other layouts that haven't changed for longer are deleted, except the demo layout and layouts with a wave that is not closed. A layout that can't be deleted at that moment (busy) is logged and tried again at the next create; the create itself goes on
SLOTTING_DEMO_SEED 0 1: on start, create the demo layout DEMO if it is missing (never changed when it exists): a fixed wall A of 6 x 8 bins (400 x 300 mm openings, 450 mm deep) and a mobile wall B of 6 x 10 bins (250 x 200 mm, 400 mm deep) standing 5.2 m to the right of A. Bin ids and light addresses are like A-01-01 (wall, row from the top, column). The demo layout is protected: replacing or deleting it, or moving its walls, answers 409 demo_protected, and if it goes missing (a flushed store) it is created again at the next layout list or create

The caps are checked before the change is written, so two requests at the same moment can both pass a cap; they are meant for a demo, not as quotas. Use them with the memory store, which a restart resets, and with an evaluation license.

The store#

Memory (trial only)#

Without Redis settings the install keeps everything in memory and logs a warning at startup. Layouts, waves and scans are lost on every restart, and the state is not shared with other instances. Use it to evaluate, never in production.

Redis#

Set REDIS_URL (or REDIS_CLUSTER_NODES, or REDIS_SENTINELS with REDIS_SENTINEL_NAME). Any Redis 6.2 or newer works, including managed services (Amazon ElastiCache or MemoryDB, Azure Cache for Redis, Google Memorystore) and Redis-compatible servers such as Valkey. Use rediss:// URLs when the server has TLS; with a private CA, add REDIS_TLS_CA_FILE.

Persistence and backups#

The plans and the scan state are operational data: if Redis loses them, the wall's contents are no longer known to the system. Configure Redis so it keeps them:

Running several instances#

For availability or load, run two or more instances against the same Redis with the same REDIS_KEY_PREFIX, behind your (private) load balancer. They share every layout and wave safely: each change to a layout takes a short lock in Redis and is written in one transaction, and each instance reloads a layout when another instance changed it. No sticky sessions are needed, and any instance can answer any scan.

Network and security#

The API has no authentication, by design: anyone who can reach the port can create and delete layouts, close waves and scan. Run the install on a private network only:

Because the API has no login, the install also protects itself against web pages that a warehouse PC happens to open:

Logs#

The install logs JSON lines to standard output: one line at startup with the store and the license, one when the license status changes, warnings (memory store, Redis connection lost and restored, a light gateway or MQTT broker that can't be reached, a webhook delivery that failed and will be retried) and errors (a webhook event given up as a dead letter). It never logs license keys, Redis passwords, AI keys, the webhook secret, light tokens or MQTT passwords, or photos.