AWS IoT Core (Claim Provisioning)

Set up AWS IoT Core fleet provisioning with claim certificates for Aether Nexus.

This guide walks through AWS IoT Core Fleet Provisioning by claim certificate for Aether, based on AWS IoT Core documentation and console workflow.

It covers cloud-side setup and the Aether-side configuration needed for first-boot device provisioning.

Provisioning flow at a glance

  1. Device connects with a claim certificate (bootstrap identity).
  2. Device requests a unique certificate with CreateKeysAndCertificate (or CreateCertificateFromCsr).
  3. Device calls RegisterThing using your provisioning template.
  4. AWS IoT creates/attaches Thing, certificate, and policy per template.
  5. Device reconnects using its permanent device certificate.

Prerequisites

  • AWS account with permissions for IoT Core and IAM.
  • OpenSSL installed.
  • Aether SDK/runtime ready on the device.
  • Chosen template name (examples use Aether).

Collect required AWS values

You need region, account-id, and endpoint for policy ARNs and Nexus config.

Get region and account-id from console

  1. Open AWS Console.
  2. Read the top-right region selector -> this is region.
  3. Open account menu (top-right) -> copy Account ID -> this is account-id.

Get region and account-id from CLI

aws configure get region
aws sts get-caller-identity --query Account --output text

Get IoT data endpoint (ATS)

Use console: AWS IoT Core -> Connect one device.

Or CLI:

aws iot describe-endpoint --endpoint-type iot:Data-ATS --query endpointAddress --output text

Expected format:

<your-iot-prefix>-ats.iot.<region>.amazonaws.com

ARN prefix format

Use this prefix in policy resources:

arn:aws:iot:<region>:<account-id>

1) Create customer CA and claim certificate (OpenSSL)

Create a local working directory:

mkdir -p aws && cd aws

Create customer CA key/certificate:

openssl genrsa -out customer-ca.key 2048
openssl req -x509 -new -nodes -key customer-ca.key -sha256 -days 3650 -out customer-ca.pem -subj "/CN=AetherCustomerCA"

Create claim key and CSR:

openssl genrsa -out claim.key 2048
openssl req -new -key claim.key -out claim.csr -subj "/CN=AetherClaimCert"

Sign claim certificate with your CA:

openssl x509 -req -in claim.csr -CA customer-ca.pem -CAkey customer-ca.key -CAcreateserial -out claim.pem -days 3650 -sha256

Verify certificate chain metadata:

openssl x509 -in customer-ca.pem -noout -subject -issuer -dates
openssl x509 -in claim.pem -noout -subject -issuer -dates

You should see:

  • claim issuer matches customer CA subject
  • validity dates are correct

If you use your own CA-signed claim certificate, register the CA in IoT Core.

In console:

  1. Go to AWS IoT Core -> Security -> CAs.
  2. Choose Register CA certificate.
  3. Upload customer-ca.pem.
  4. Prefer multi-account / SNI_ONLY mode (AWS recommendation), when applicable.
  5. Activate the CA certificate.

CLI alternative (SNI_ONLY mode):

aws iot register-ca-certificate --ca-certificate file://customer-ca.pem --certificate-mode SNI_ONLY
aws iot update-ca-certificate --certificate-id <ca-certificate-id> --new-status ACTIVE

3) Provisioning role in UI flow

When you create the template through Connect many devices -> Provisioning templates -> Provisioning devices with claim certificates, AWS IoT can create and configure the provisioning role for you in the wizard.

Use the auto-created role unless your organization requires a pre-existing custom IAM role.

Optional: advanced teams can still create/manage this role manually, but this guide assumes the UI-managed role path.

4) Create fleet provisioning template (console path)

In AWS Console:

  1. Go to AWS IoT Core.
  2. Open Connect many devices.
  3. Open Provisioning templates.
  4. Choose Create provisioning template.
  5. Select Provisioning devices with claim certificates.
  6. Set template name (for example Aether).
  7. In the role step, choose the option to let AWS IoT create/manage the provisioning role automatically.
  8. Continue to claim certificate and permissions steps.

5) Create claim-certificate policy (bootstrap permissions)

Attach this policy to the claim certificate. Replace:

  • <region>
  • <account-id>
  • <template-name>
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Publish",
        "iot:Receive"
      ],
      "Resource": [
        "arn:aws:iot:<region>:<account-id>:topic/$aws/certificates/create/*",
        "arn:aws:iot:<region>:<account-id>:topic/$aws/provisioning-templates/<template-name>/provision/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": [
        "arn:aws:iot:<region>:<account-id>:topicfilter/$aws/certificates/create/*",
        "arn:aws:iot:<region>:<account-id>:topicfilter/$aws/provisioning-templates/<template-name>/provision/*"
      ]
    }
  ]
}

This policy intentionally allows only bootstrap/provisioning topics.

6) Upload claim credentials in template wizard

In the claim certificate step:

  • CA certificate: customer-ca.pem
  • claim cert: claim.pem
  • claim private key: claim.key

Then ensure:

  • claim certificate is ACTIVE
  • claim certificate has claim policy from Step 5 attached

7) Configure device policy (for provisioned certificates)

In Set device permissions, create/use a device policy like:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "arn:aws:iot:<region>:<account-id>:client/${iot:ClientId}"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Publish",
      "Resource": "arn:aws:iot:<region>:<account-id>:topic/${iot:ClientId}"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": "arn:aws:iot:<region>:<account-id>:topicfilter/${iot:ClientId}/*"
    },
    {
      "Effect": "Allow",
      "Action": "iot:Receive",
      "Resource": "arn:aws:iot:<region>:<account-id>:topic/${iot:ClientId}/*"
    }
  ]
}

This policy is attached to each newly provisioned device certificate.

8) Configure Aether Nexus

Configure Nexus with claim credentials and template name:

from aether.nexus import Nexus

nexus = Nexus(
    opc_ua_url="opc.tcp://0.0.0.0:4840",
    token="<your-aether-token>",
    aws_iot_endpoint="<your-iot-prefix>-ats.iot.<region>.amazonaws.com",
    aws_iot_template_name="Aether",
    aws_iot_claim_cert="./aws/claim.pem",
    aws_iot_claim_key="./aws/claim.key",
)

Required alignment:

  • aws_iot_endpoint must be your account ATS endpoint
  • aws_iot_template_name must exactly match template name in IoT Core
  • claim cert/key paths must match files provisioned on disk

9) Sigil read/write MQTT event behavior

After provisioning completes and Nexus is running with AWS IoT enabled, each Sigil operation is emitted as an MQTT message:

  • Every Sigil read operation publishes one event message.
  • Every Sigil write operation publishes one event message.
  • Topic uses the device Thing name (for example <thing-name>).

Event payload format:

class SigilEvent(BaseModel):
    """Event data for a sigil read or write event."""

    sigil_id: str
    node_id: str
    value: Any
    timestamp: datetime
    operation: Literal["read", "write"]

Notes:

  • operation is read for reads and write for writes.
  • timestamp is the event generation time.
  • node_id and sigil_id identify which Sigil produced the event.
  • Ensure your device runtime uses the same naming convention for MQTT client identity and Thing topic routing so events are delivered under the expected Thing topic.

10) Why not use AWS IoT Core for real-time control

Use AWS IoT Core for telemetry, events, and asynchronous commands, not hard real-time control loops.

Why:

  1. Asynchronous brokered messaging, not deterministic control transport
    MQTT in AWS IoT Core is pub/sub with broker mediation, retries, session state, and queueing behavior. This is excellent for resilience, but it does not provide deterministic command-execution timing guarantees.
  2. Callback + queue processing model in device SDK/runtime
    In practice, MQTT clients process inbound messages through callback handlers scheduled by background/event-loop workers. Commands are executed when they are dequeued and handled by your runtime, not at a guaranteed control-cycle deadline.
  3. No enforceable execution deadline at broker level
    You can tune QoS/timeouts, but you cannot force "execute exactly at T" semantics through AWS IoT Core. Network jitter, broker scheduling, reconnect behavior, and client-side processing all affect timing.
  4. AWS IoT service characteristics are throughput/reliability oriented
    AWS documents broker limits and asynchronous behavior (for example delayed processing when limits are exceeded, queued message replay rates, and ordering caveats in some flows). These characteristics are compatible with messaging systems, not hard real-time actuation.

Recommended architecture:

  • Keep closed-loop control local (PLC/RT controller/edge process with deterministic scheduler).
  • Use AWS IoT Core to publish state/events and receive high-level intents.
  • Treat cloud-to-device actions as eventual commands, with local safety interlocks and timeout/fallback logic.

11) Complete example: periodic Sigil reads via Weave

The following script is a complete example that periodically reads one Sigil every 2 seconds.
Each await temperature.read() generates a Sigil read event, which is published to AWS IoT Core on the Thing-name topic.

from aether.nexus import Nexus
from aether.sigil import Sigil
from aether.weave import Weave

# 1) Configure Nexus with AWS IoT fleet provisioning (claim cert path)
nexus = Nexus(
    opc_ua_url="opc.tcp://0.0.0.0:4840",
    token="<your-aether-token>",
    aws_iot_endpoint="<your-iot-prefix>-ats.iot.<region>.amazonaws.com",
    aws_iot_template_name="Aether",
    aws_iot_claim_cert="./aws/claim.pem",
    aws_iot_claim_key="./aws/claim.key",
)

# 2) Define a Sigil that you want to poll periodically
temperature = Sigil(
    node_id="ns=2;s=Plant.Temperature",
    initial_value=25.0,
    description="Process temperature value.",
)


# 3) Periodic task: read Sigil every cycle
async def publish_temperature_read_event():
    value = await temperature.read()
    print(f"Sigil read -> Plant.Temperature={value}")


# 4) Register weave at module scope (before nexus.start)
Weave(
    label="temperature-read-poller",
    cycle_time=2.0,
    callback=publish_temperature_read_event,
    description="Reads temperature every 2 seconds to emit Sigil read MQTT events.",
)


if __name__ == "__main__":
    nexus.start()

What to verify:

  1. Run the script and wait for provisioning/reconnect to complete.
  2. Open AWS IoT Core -> MQTT test client.
  3. Subscribe to the Thing topic (for example <thing-name>).
  4. Confirm you receive periodic messages with:
    • operation: "read"
    • node_id: "ns=2;s=Plant.Temperature"
    • updated timestamp

12) Validate provisioning end-to-end

  1. Start Nexus/device process with claim cert.
  2. Confirm device can complete first bootstrap and reconnect.
  3. In IoT Core, verify:
    • new Thing created
    • new device certificate created and ACTIVE
    • device policy attached to new certificate
  4. Trigger Sigil reads/writes and verify SigilEvent messages are published under the Thing-name topic.

Troubleshooting

  • Not authorized on bootstrap connect:
    • claim cert missing policy, inactive cert, or wrong endpoint.
  • RegisterThing rejected:
    • wrong template name, provisioning role not granted correctly, or malformed template parameters.
  • ARN mismatches:
    • re-check <region> and <account-id> in both claim and device policies.
  • Provisioning works once then fails:
    • claim cert deactivated or rotated without updating device files.
  • No new thing appears:
    • inspect IoT logs and CloudWatch logs for provisioning errors.

Security recommendations

  • Protect private keys (customer-ca.key, claim.key) with strict filesystem permissions.
  • Do not commit cert/key material to source control.
  • Consider short-lived or batch-scoped claim cert strategy and rotation.
  • Monitor claim-certificate usage via IoT logs/metrics; deactivate quickly on suspected misuse.
  • For stricter onboarding control, add a pre-provisioning Lambda hook to validate serial/device attributes before allowing provisioning.

References