Anycubic (Kobra family) β LAN Protocol¶
Status: π’ hardware-validated (Kobra X
20030fw1.2.0.2; Kobra 320024write path) Β· Firmware: Klipper-based, Anycubic "avata" / "gkapi" cloud stack Β· Models: Kobra X, Kobra 2 / 3 / S1 (+ variants)Anycubic printers are driven over a per-printer MQTT session on TLS
:9883, with a plain-HTTP:18910side-channel for identity and file upload. Two closely-related dialects β avata (Kobra X) and gkapi (Kobra 2/3/S1) β share one command set and differ only in a few report fields.
At a glance¶
- Transport: MQTT over TLS
:9883(client presents a slicer cert; the printer's own server cert is not verified). Plus plain-HTTP:18910for identity + file upload. - Discovery: manual IP; identity via
GET http://<ip>:18910/info. A sleeping printer goes HTTP-silent on:18910. - Auth / credential: the MQTT username/password/device-id are derived from the printer via
/info+/ctrl; the mTLS client cert/key come from the user's own Anycubic slicer install (never shipped here β see Credentials). - Read / status: poll-hybrid β an idle printer pushes nothing (so poll ~every 48 s); an active one auto-pushes temperature + print reports.
- File transfer: multipart
POST http://<ip>:18910/gcode_upload(needs anX-File-Lengthheader). - Print launch: two-step β HTTP upload, then MQTT
print:start. - Feeders: ACE multi-material (
multiColorBox) + single external spool (extfilbox). - β οΈ The load-bearing gotcha: a command's action goes in the JSON payload, never in the topic. Append the
action to the topic and the printer silently drops it (
messageHandler not found) while still ACKing β it looks like it worked.
Transport & connection¶
Per-printer MQTT on :9883, TLS. The client presents an mTLS client cert (the Anycubic slicer's cert/key) but
sets verify_mode = CERT_NONE and check_hostname = False β the printer serves a self-signed, expired
O=AnyCubic cert and negotiates legacy TLS, so the client also lowers the OpenSSL security level
(DEFAULT:@SECLEVEL=0). MQTT username/password are set; keepalive 60 s. π‘
Message envelope β compact JSON, {",":"}-separated:
{"type":"<msgType>","action":"<verb>","msgid":"<uuid4>","timestamp":<epoch_ms>,"data":<any>}
{"state":β¦, "code":β¦, "msg":β¦} (emitted only when present). msgid is a
UUIDv4; timestamp is epoch milliseconds. π’
Topic grammar: anycubic/anycubicCloud/v1/<channel>/<modelId>/<deviceId>/<msgType> β the action is in the
payload, not the topic (see the gotcha above). Channels:
| Channel | Use |
|---|---|
slicer/printer |
slice/launch flow β print:start, multiColorBox:getInfo |
web/printer |
all live control + monitoring |
printer/public |
printer reports; subscribe the wildcard β¦/printer/public/<model>/<device>/# |
Correlation is by echoed msgid (with a fallback to a report-topic suffix for getInfo-style replies). Every command
is ACKed with a bare {"msgid":β¦} on a β¦/response topic. π’
Discovery & identity¶
Plain-HTTP GET http://<ip>:18910/info returns the identity (tolerant of a bare object or a {"code":200,"data":{β¦}}
wrapper). Golden shape (values synthetic): π’
{"deviceName":"Kobra X","deviceType":"FDM","modelId":"20030","modelName":"Kobra X","ip":"192.0.2.10",
"rtspUrl":"rtsp://192.0.2.10/live","fileUploadurl":"http://192.0.2.10:18910/gcode_upload"}
/info omits fileUploadurl (take it from the MQTT
report's urls.fileUploadurl instead); gkapi advertises it.
Credentials / auth¶
Two credentials, both user-owned:
- The mTLS client cert/key live inside the user's own Anycubic slicer (
cloud_mqtt.dllin the slicer install). This orchard documents that they live there; it never ships them. An integration extracts them from the user's own installation. π‘ - The MQTT username/password/device-id are derived at connect time:
GET /infoyields atoken; thenPOST /ctrlwith a signed request returns an AES-CBC-encrypted config blob carrying the credentials. The signature ismd5( md5(token[:16]) + ts + nonce ); the config is decrypted withkey = token[16:32], IV = the response'stoken. π’
Reading state¶
Poll-hybrid, ~48 s baseline. An idle printer emits nothing unsolicited, so a client polls a read-only query set
(print:query, status:query, tempature:query, fan:query, light:query, multiColorBox:getInfo). While
printing, the printer auto-pushes tempature and print reports; the poll is just a backstop. π’
Report types: info (rich snapshot), status, tempature (sic β the misspelling is the real verb), fan,
light, print, file, peripherie, multiColorBox, and an empty-type heartbeat.
- Temperatures: plain Β°C (
curr_/target_nozzle_temp,curr_/target_hotbed_temp). π’ - Progress:
progress0β100, time-based β a real time fraction (unlike file-byte-position progress on some brands). π’ - Times:
print_time/remain_timeare in minutes (Γ60 for seconds). (A common cross-brand trap β see../patterns/timing-normalization.md.) π’ - State (native β normalized):
freeβstandby;busy/printing/auto_leveling/resumingβprinting;preheatingβpreheating;pausing/pausedβpaused;stoped(sic)/canceledβcancelled;finishedβcomplete;failedβerror. For aninforeport,project.statetakes precedence over the top-levelstate. π’
Writing / control¶
Command verbs (exact type:action) β all on web/printer unless noted:
print:start (slicer/), print:pause/resume/stop/update, tempature:set (sic), fan:setSpeed (not fan:set),
light:control, axis:move, axis:turnOff (β M84), skip:start, info:setPrinterName,
video:startCapture/stopCapture, plus the feeder verbs below. π’
Print launch (two-step): π’
- multipart
POST <fileUploadurl>with the file fieldgcode, afilenamefield, and headerX-File-Length: <raw byte length>. Must be a real.gcode.3mf. Success ={"code":200,"data":{"gcode":<stored_name>}}. - MQTT
print:startonslicer/printerreferencing the uploaded file (payload carriesfilename,md5, and anams_box_mappingfor multi-material).
β οΈ Control writes drive a hot, moving machine β validate against your own device and gate them behind an explicit "enable writes" in any client.
Multi-material / feeders¶
- ACE (
multiColorBox): a bank of slots feeding one nozzle. Per-slot fields:type,color(RGB),status(==5= loaded),sku,weight(g),consumables_percent. Boxes carry anid(-1= the stock 4-slot toolhead,0..3= ACE units) and amodel_id(ACE Gen 2 =40002). Note brand/RFID identity is not on the LAN wire β it's only reachable over the ACE's USB link. π’ - extfilbox: a single external spool; its
loadedflag doubles as the runout signal. π’ - See
../patterns/multi-material-feeders.mdfor the cross-vendor model.
Quirks & gotchas¶
- Action-in-payload (the big one, above).
- The verb is
tempature(misspelled) andfan:setSpeedβ spelling matters on the wire. - Server TLS cert is expired/self-signed by design β verify-off + legacy ciphers are required to connect.
- avata
/infoomits the upload URL; read it from the MQTT report. - A sleeping printer is simply HTTP-silent on
:18910(not an error to panic on).
Confidence & validation¶
- π’ Validated on a real Kobra X (
20030, fw1.2.0.2) for the read path and on a Kobra 3 (20024) for the write/command path (12 commands captured byte-for-byte). - π‘ The
/ctrlcredential derivation is source-read + a synthetic round-trip; the exact live response codes aren't pinned. - Open gaps (a capture would close them): the avata ACE
multiColorBoxlive shape (validated on gkapi, assumed identical); the keep-alive sub-protocol; other model ids (Kobra 4 / S1 variants).
Sources¶
Clean-room, facts-only. Extracted from Anycubic's own open-source slicer (the "SlicerNext" OrcaSlicer fork) device
layer + a plain-HTTP :18910 inspection + sanitized MQTT captures from an owner's Kobra X / Kobra 3. No slicer code
was copied; no certificate/key/credential values are included (the cert lives in the user's own slicer install). Passed
../CLEANROOM-CHECKLIST.md.