AWS IoT Data Plane
AWS IoT Data Plane (iotdata) on fakecloud: device shadows + retained messages, a complete 11-operation data plane (100% conformance). restJson1.
fakecloud implements the AWS IoT Data Plane (iotdata, SDK id IoT Data Plane, endpoint prefix data-ats.iot) as a restJson1 service. All 11 operations ship with 100% conformance against AWS's own Smithy model, backed by account-partitioned state that persists across restarts in persistent mode. IoT Data Plane signs SigV4 with the iotdata scope and is routed by HTTP method plus @http URI path (POST /things/{thingName}/shadow, GET /things/{thingName}/shadow, POST /topics/{topic}, GET /retainedMessage/{topic}, ...).
This is a real device-shadow state machine and retained-message store, not a set of stubs: UpdateThingShadow really merges, versions, and computes deltas; GetThingShadow returns exactly what was merged; and retained messages are stored and read back byte-for-byte.
Device shadows
A shadow is a persistent JSON document representing a thing's state. Each thing has one classic (unnamed) shadow plus any number of named shadows. Shadows are stored by thingName string; fakecloud does not require the thing to pre-exist in an IoT Core registry.
UpdateThingShadow(POST /things/{thingName}/shadow, optional?name=<shadowName>) accepts a shadow document as the raw@httpPayloadbody:{"state":{"desired":{…},"reported":{…}}}. The update is deep-merged into the stored shadow (anullleaf deletes that key, matching AWS; nested objects merge recursively, everything else overwrites). On each successful update theversionincrements by one, per-leafmetadatatimestamps are stamped for every touched leaf, andstate.delta(the subset ofdesiredthat differs fromreported) is recomputed. The response payload is the resulting document.GetThingShadow(GET /things/{thingName}/shadow, optional?name=) returns the full shadow document:state(desired/reported/delta),metadata,version, andtimestamp. A missing shadow isResourceNotFoundException.DeleteThingShadow(DELETE /things/{thingName}/shadow, optional?name=) removes the shadow and returns the deletion payload (version+timestamp).ListNamedShadowsForThing(GET /api/things/shadow/ListNamedShadowsForThing/{thingName}) returns a thing's named-shadow names, paginated bypageSizewith a round-trippingnextToken.
Version conflicts
An UpdateThingShadow may carry a version for optimistic locking. If it does not match the shadow's stored version, the update is rejected with ConflictException (HTTP 409).
Publish + retained messages
Publish(POST /topics/{topic},?qos=,?retain=) accepts an MQTT message for a topic. Whenretain=truethe message is stored (an empty retained payload clears the topic, matching AWS); a non-retained publish is accepted as a 200 no-op.GetRetainedMessage(GET /retainedMessage/{topic}) returns a stored retained message (payload,qos,lastModifiedTime), orResourceNotFoundExceptionwhen the topic has none.ListRetainedMessages(GET /retainedMessage) paginates the retained-topic summaries (topic,payloadSize,qos,lastModifiedTime) bymaxResults.
Validation & errors
Model-derived validation rejects contract violations with the codes the model declares: InvalidRequestException (malformed shadow document, out-of-range qos), ResourceNotFoundException (missing shadow / retained message / connection), ConflictException (shadow version conflict), plus the declared ThrottlingException / UnauthorizedException / MethodNotAllowedException / RequestEntityTooLargeException / ServiceUnavailableException / InternalFailureException family. State is account-partitioned and persisted.
Honest gaps
fakecloud runs no MQTT broker, which shapes two areas honestly:
- No live pub/sub fan-out.
Publishis accepted (and, for retained messages, stored), but it is never delivered to subscribed MQTT clients. There is no live broker to fan messages out to. - No connected clients. Because there is no broker, no MQTT client is ever connected. The connection-introspection operations (
GetConnection,DeleteConnection,ListSubscriptions, andSendDirectMessage) therefore faithfully returnResourceNotFoundExceptionfor every client id, which is exactly what AWS returns for an unknown connection. This is a real response reflecting the emulated state, not a fabricated connection record.
The device-shadow merge/delta/version state machine and the retained-message store are fully real; these gaps are confined to live MQTT transport, which has no in-process equivalent.