Hands on with Matter and Thread(11 min read)

Matter and Thread offer many benefits, with standardised interoperability, local-only control, built-in security, multi-admin, and IPv6 support — helping drive IPv6 adoption and development skills.

Several devices have now launched, and I have tried out a few of the available devices with Google Home and Home Assistant, however these are early days, and feature implementation still lags behind native integrations in some significant areas.

Thread-based devices:

  • Nanoleaf Essentials light bulb and LED light strip
  • Eve Home smart plug

Wi-Fi devices:

  • Sonoff MINIR4M inline switch
  • Zemismart ZME2 dual inline switch
  • Wiz light bulbs
  • Tapo P110m smart plug

Most devices initially required their native app for firmware upgrades (although the new Eve device updated without it), and there were many features only accessible via native apps (even where the features are in the Matter standard).

In particular none of the switches had separate switch and relay parts for detached operation via Matter bindings, although the Sonoff does support detached mode via the native app, and the Zemismart had the Binding cluster but I couldn't get it working.

Continue reading Hands on with Matter and Thread(11 min read)

Deploying a secure LwM2M IPv6 test server on AWS(15 min read)

Lightweight Machine-to-Machine (LwM2M) is a compact protocol design for Internet-of-Things (IoT) scenarios, that provides end-to-end services including efficient transport, encryption, device lifecycle, and messaging semantics. Devices deployed to the field will connect to full LwM2M endpoints, however you may also want to deploy your own LwM2M demo server for testing purposes.

This article shows you how to deploy an Eclipse Leshan server onto Amazon Web Services (AWS), configured for secure connections (COAPS for messaging, and HTTPS with basic authentication for the Web UI), accessible over the internet, and including support for both IPv6 and legacy IPv4.

First we will configure a network in AWS, then deploy the server, and then test the deployment.

The instructions below show the detail of building the deployment, but if you want a quick start to just get it up and running see the instructions on Github https://github.com/sgryphon/iot-demo-build/blob/main/aws-leshan/README-aws-leshan.md

To prepare settings and deploy the CloudFormation stacks:

aws sso login

$keyName = "leshan-demo-key".ToLowerInvariant()
$sshFolder = "~/.ssh"
$keyPath ="$sshFolder/$keyName.pem"
aws ec2 create-key-pair --key-name $keyName --query 'KeyMaterial' --output text | Out-File $keyPath

$ENV:CDK_DEFAULT_ACCOUNT = ${ENV:AWS_PROFILE}.Split('-')[1]
$ENV:CDK_DEFAULT_REGION = $ENV:AWS_DEFAULT_REGION
cdk deploy Lwm2mDemoNetworkStack
cdk deploy Lwm2mDemoServerStack --parameters Lwm2mDemoServerStack:basicPassword=YourSecretPassword

Read on for the full details.

AWS container diagram

CloudFormation and the Cloud Development Kit (CDK)

The deployment uses the Cloud Development Kit (CDK), which is a programming language based wrapper around CloudFormation templates.

The generated infrastructure is deployed via CloudFormation in AWS. For more detail see https://aws.amazon.com/cdk/

You need to install and bootstrap the Cloud Development Kit to run the above deployments.

IPv6 network configuration in AWS

The first stack deployed is a base network.

Network configuration

The only configuration parameter for the network is the private address range used. As this demo network is not intended to be connected to anything, the range used does not matter a lot.

export interface Lwm2mDemoNetworkStackProps extends cdk.StackProps {
  readonly ipv4PrivateAddresses?: IIpAddresses;
}

Base network creation

We use the level 2 VPC construct in the Cloud Development Kit library to get a basic network structure. By default this deploys one public and private network per availability zone. For this demo we are only deploying one zone (two networks), although the default for redundancy is at least three.

We also manually create the NAT provider and pass it in, so we can reference it later. Although we aren't using the private network, leaving the default will create a NAT gateway, using the provider, for us.

const maxAzs = 1;
const natProvider = NatProvider.gateway();
this.vpc = new Vpc(this, 'VPC', {
    ipAddresses: props?.ipv4PrivateAddresses,
    maxAzs: maxAzs,
    natGatewayProvider: natProvider,
});

Adding IPv6 to the virtual private cloud

We assign an Amazon-provided /56 block to the VPC for the public network.

If we were also using the private network we could assign a second /56 block, to keep the ranges organised. IPv6 addresses are far more plentiful than IPv4, so rather than fiddling trying to balance netmask sizes, we can just add more blocks as needed.

We also tag the network as dual stack, as good pratice.

const ipv6PublicBlock = new CfnVPCCidrBlock(this.vpc, 'Ipv6PublicBlock', {
    amazonProvidedIpv6CidrBlock: true,
    vpcId: this.vpc.vpcId
});
Tags.of(this.vpc).add('aws-cdk-ex:vpc-protocol', 'DualStack',
  { includeResourceTypes: [CfnVPC. CFN_RESOURCE_TYPE_NAME] });

If we were also going to configure private subnets, then we would also add an IPv6 egress-only gateway to the VPC.

Subnet configuration for IPv6

We need to loop through and update each of the default created public subnets (we will only have one) to support IPv6. Public networks need to be dual-stack, so that an IPv4 address can be assigned to the NAT Gateway, so that we can enable NAT64.

Other changes to the network:

  • Auto-assign IPv6 addresses and disable automatic mapping of public IPv4 addresses. AWS will now be charging for public IPv4 addresses, so we only want them used when explicitly added.
  • Assign an IPv6 /64 subnet, just numbering them sequentially. This uses the CloudFormation CIDR function to generate the values at deploy time (you can see the function used in the synthesized template).
  • A dependency to the IPv6 block is also added, so that deploy/destroy is done in the correct sequence.
  • Enable DNS64, so that IPv6-only machines on the network will be able to access external IPv4 destinations using NAT64.

We also update the route for the public network to connect IPv6 traffic to the internet gateway, and also a NAT64 route to the NAT Gateway.

const ipv6CidrBlocks = Fn.cidr(
  Fn.select(0, this.vpc.vpcIpv6CidrBlocks),
  maxAzs,"64");
this.vpc.publicSubnets.forEach((subnet, index) => {
    Tags.of(subnet).add('aws-cdk-ex:subnet-protocol', 'DualStack', { includeResourceTypes: [CfnSubnet.CFN_RESOURCE_TYPE_NAME] });

    const cfnSubnet = subnet.node.defaultChild as CfnSubnet;
    cfnSubnet.assignIpv6AddressOnCreation = true;
    cfnSubnet.enableDns64 = true;
    cfnSubnet.ipv6CidrBlock = Fn.select(index, ipv6CidrBlocks);
    cfnSubnet.mapPublicIpOnLaunch = false;
    cfnSubnet.privateDnsNameOptionsOnLaunch = {
        EnableResourceNameDnsAAAARecord: true,
        EnableResourceNameDnsARecord : true
    };
    cfnSubnet.addDependency(ipv6PublicBlock);

    const sn = subnet as Subnet;
    sn.addRoute('Ipv6Default', {
        destinationIpv6CidrBlock: '::/0',
        routerId: this.vpc.internetGatewayId!,
        routerType: RouterType.GATEWAY,
    });

    const natGatewayId = natProvider.configuredGateways[index].gatewayId;
    sn.addRoute('Nat64', {
        destinationIpv6CidrBlock: '64:ff9b::/96',
        routerId: natGatewayId,
        routerType: RouterType.NAT_GATEWAY,
    });
});

An private dual stack network would have similar alterations, using the Egress-only gateway for IPv6. For simpler configuration, private networks can also be IPv6 only.

Tagging and output

Best practice is to apply relevant identifcation tags when resources are created.

We also register the key identifiers as outputs, so they will be available in the stack output.

Tags.of(this).add('Owner', 'IoT Demo');
Tags.of(this).add('Classification', 'Confidential');

new CfnOutput(this, 'VpcId', { value: this.vpc.vpcId });
new CfnOutput(this, 'PublicSubnetIds', { value: this.vpc.publicSubnets.map(x => x.subnetId).join(',') });

AWS VPC created

Leshan server deployment and configuration

This stack will set up an EC2 Instance, connect it to the network with appropriate security permissions, and then install the Java runtime and download the Leshan server ready to install.

The Caddy 2 reverse proxy is also installed on the instance, to provide HTTPS support and basic authentication for the Leshan web interface.

Server configuration

The configuration details include the address suffix (100d) for the server, the instance type, SSH key name, and a reference to the VPC from the network stack.

If you have your own DNS, you can configure a known entry for the IPv6 address of server, based on the prefix assigned to the network and the assigned suffix. Passing in the host name will configure Caddy to provide HTTPS for the name.

If you don't have your own DNS, then leave this blank. AWS only provides DNS names for IPv4 addresses, so you will have to use IPv4 for the web interface (but can use IPv6 for the device connection). If hostName is black, then script will auto-generate based on the name assigned by AWS to the IPv4 address.

export interface Lwm2mDemoServerStackProps extends cdk.StackProps {
  readonly addressSuffix?: string;
  readonly hostName?: string;
  readonly instanceType?: InstanceType;
  readonly keyName?: string;
  readonly vpc?: Vpc;
}

The deployment also has a CloudFormation parameter for the web UI password:

// Get password from parameter
const basicPassword = new CfnParameter(this, "basicPassword", {
  type: "String",
  description: "Password used to secure the Leshan web interface",
});

Server role

The deployment creates a role for the server, with good practice configuration for CloudWatch logs.

const serverRole = new Role(this, 'serverEc2Role', {
  assumedBy: new ServicePrincipal('ec2.amazonaws.com'),
  inlinePolicies: {
    ['RetentionPolicy']: new PolicyDocument({
      statements: [
        new PolicyStatement({
          resources: ['*'],
          actions: ['logs:PutRetentionPolicy'],
        }),
      ],
    }),
  },
  managedPolicies: [
    ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'),
    ManagedPolicy.fromAwsManagedPolicyName('CloudWatchAgentServerPolicy'),
  ],
});

Network security

We allow inboud LwM2M on ports 5683 and 5684, for both IPv4 and IPv6. We also allowing incoming HTTP and HTTPS traffic for the web UI (ports 80 and 443), and enable Ping for basic testing.

As this is a demo server, we also configure SSH access, so that we can log in and run the Leshan demo server.

this.securityGroup = new SecurityGroup(this, 'Lwm2mDemoSecurityGroup', {
  vpc: props!.vpc!,
  description: 'Security Group for LwM2M demo server',
  allowAllOutbound: true,
  allowAllIpv6Outbound: true,
});
this.securityGroup.addIngressRule(Peer.anyIpv6(), Port.tcp(22), 'Allow IPv6 SSH (22) inbound');
this.securityGroup.addIngressRule(Peer.anyIpv6(), Port.tcp(80), 'Allow IPv6 HTTP (80) inbound')
this.securityGroup.addIngressRule(Peer.anyIpv6(), Port.tcp(443), 'Allow IPv6 HTTPS (443) inbound')
this.securityGroup.addIngressRule(Peer.anyIpv6(), Port.udpRange(5683, 5684), 'Allow IPv6 LwM2M (5683-5684) inbound')
this.securityGroup.addIngressRule(Peer.anyIpv6(), Port.allIcmpV6(), 'Allow IPv6 ICMP inbound');
this.securityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(22), 'Allow IPv4 SSH (22) inbound');
this.securityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(80), 'Allow IPv4 HTTP (80) inbound')
this.securityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(443), 'Allow IPv4 HTTPS (443) inbound')
this.securityGroup.addIngressRule(Peer.anyIpv4(), Port.udpRange(5683, 5684), 'Allow IPv4 LwM2M (5683-5684) inbound')
this.securityGroup.addIngressRule(Peer.anyIpv4(), Port.icmpPing(), 'Allow IPv4 ICMP ping inbound');

DNS host name, for Caddy configuration

If a manual host name is not provided, we convert the Elastic IP public address into the corresponding DNS entry, by combining it with the region name.

var eip = new CfnEIP(this, "Ip");
var hostName = props?.hostName;
if (!hostName) {
  hostName = 'ec2-' + Fn.join('-', Fn.split('.', eip?.attrPublicIp!)) + '.'
    + Stack.of(this).region + '.compute.amazonaws.com'
}

Cloudinit configuration

This configures the server software, with two main parts: Caddy and Leshan.

Caddy comes from a community project (COPR), so the relevant repository needs to be enabled for installation. Using cloud-init a user is created to run the service, and then a template configuration file is created, injecting the public DNS host name of the server.

Additional shell commands then hash the provided secret password and insert it into the basicauth settings of the configuration file. The configuration is done in two passes, with the DNS host name inserted from the CDK/CloudFormation template, and then the hashed password set by a shell command on the server using caddy hash-password and sed (with the command containing the injected password parameter).

Note the shell encoding of the environment variable when passing to sed -- we are using : as the separator and then using shell encoding to escape any : in the hashed value (originally the problem was with the base64 hash containing / characters and using / as the traditional separator; using : should not clash, but we are escaping anyway just in case it changes).

For Leshan, we first install the Java runtime from the system package, and then download the Leshan server files.

const init = CloudFormationInit.fromConfigSets({
  configSets: { default: ['caddy', 'leshan'], },
  configs: {
    caddy: new InitConfig([
      InitUser.fromName('caddy', { 
        homeDir: '/var/lib/caddy',
      }),
      InitFile.fromString('/etc/caddy/Caddyfile',
        hostName + ' {\n'
        + '  basicauth {\n'
        + '    iotadmin __hashed_password_base64__\n'
        + '  }\n'
        + '  reverse_proxy localhost:8080\n'
        + '}\n',
        { group: 'caddy', owner: 'caddy', }
      ),
      InitCommand.shellCommand('sudo yum -y copr enable @caddy/caddy epel-7-$(arch)'),
      InitCommand.shellCommand('sudo yum -y install caddy'),
      InitCommand.shellCommand('HASHED_PASSWORD=$(caddy hash-password --plaintext \'' + basicPassword.valueAsString + '\');'
        + ' echo $HASHED_PASSWORD;' 
        + ' sudo sed -i s:__hashed_password_base64__:${HASHED_PASSWORD//:/\\:}:g /etc/caddy/Caddyfile'),
      InitCommand.shellCommand('sudo systemctl enable --now caddy'),
    ]),
    leshan: new InitConfig([
      InitPackage.yum('java-17-amazon-corretto'),
      InitCommand.shellCommand('mkdir /home/ec2-user/leshan-server'),
      InitCommand.shellCommand('wget -O /home/ec2-user/leshan-server/leshan-server-demo.jar https://ci.eclipse.org/leshan/job/leshan-1.x/lastSuccessfulBuild/artifact/leshan-server-demo.jar'),
    ]),
  }
});

If there are any issues with configuration, you can check the cloud-init logs. Here we can see how NAT64 is used to download the Leshan server package from the IPv4 only Eclipse server:

Cloud-init logs showing NAT64

Creating the server instance

A few other configuration parameters are specified, and then the server instance is created, using the cloud-init defined above, the required security group, and the SSH key name from the passed in configuration.

const az = cdk.Stack.of(this).availabilityZones[0];
const subnetSelection: SubnetSelection = {
  subnetType: SubnetType.PUBLIC,
  availabilityZones: [ az ],
};

const machineImage = MachineImage.latestAmazonLinux2023({
  cachedInContext: false,
  cpuType: AmazonLinuxCpuType.X86_64,
});

this.instance = new Instance(this, 'Instance', {
  init: init,
  initOptions: {
    ignoreFailures: true,
    timeout: Duration.minutes(10),
  },
  instanceType: props?.instanceType!,
  keyName: props?.keyName,
  machineImage: machineImage,
  securityGroup: this.securityGroup,
  role: serverRole,
  userDataCausesReplacement: true,
  vpc: props!.vpc!,
  vpcSubnets: subnetSelection,
});

Assign public IPv4

We associate the public IPv4 address with the instance. This association is separate from the instance itself, allowing new instances to be created and then the association moved across, keeping the same public IPv4 address.

// Assign Elastic IPv4
const ec2Assoc = new CfnEIPAssociation(this, "Ec2Association", {
  eip: eip!.ref,
  instanceId: this.instance.instanceId
});

Assign a known IPv6 address

We also assign a known IPv6 address, from the network prefix and a static suffix, so that the server is easy to reference (e.g. set up DNS).

The default network interface on the instance (above) will have a private IPv4 address, and will also be automatically assigned a random IPv6 address.

The known IPv6 address is then assigned to a second network interface and attached to the instance, similar to IPv4 Elastic IP association.

This allows CloudFormation to replace the instance but keep the known IPv6 address: a new instance is first created (with a random IPv6 address), the network interface attachment is then moved from the old instance to the new, and then the old instance is deleted.

If we try to assign the known address directly to the instance then it can not be easily redeployed as the address is not available to create the replacement whilst the old instance is still running. Having the network interface (holding the address) as a separate resource allows it to be moved.

const vpcBlock = 0;
const subnetBlock = 0;
const addressBlock = Fn.select(subnetBlock, 
  Fn.cidr(Fn.select(vpcBlock, props?.vpc?.vpcIpv6CidrBlocks!), subnetBlock + 1, "64"));
const split = Fn.split(":", addressBlock)
const ipv6Address = Fn.join(":",
  [ Fn.select(0, split), Fn.select(1, split), Fn.select(2, split), Fn.select(3, split),
    "", props?.addressSuffix! ]);

const networkInterface = new CfnNetworkInterface(this, 'Network', {
  ipv6Addresses: [ { ipv6Address: ipv6Address } ],
  groupSet: [ this.securityGroup.securityGroupId ],
  subnetId: props?.vpc?.selectSubnets(subnetSelection).subnetIds[0]!,
});

const networkAttachment = new CfnNetworkInterfaceAttachment(this, 'NetworkAttachment', {
  deviceIndex: '1',
  instanceId: this.instance.instanceId,
  networkInterfaceId: networkInterface.attrId,
});

Server output parameters

There instance ID is created as a named output, so that we can reference it to obtain the runtime details.

new CfnOutput(this, 'instanceId', { value: this.instance.instanceId });

Testing the deployment

After the creation process, you can query the instance details via the AWS CLI:

$leshanStack = aws cloudformation describe-stacks --stack-name Lwm2mDemoServerStack | ConvertFrom-Json
$leshanInstance = aws ec2 describe-instances --instance-ids $leshanStack.Stacks[0].Outputs[0].OutputValue | ConvertFrom-Json
$leshanInstance.Reservations.Instances.NetworkInterfaces.Ipv6Addresses.Ipv6Address, $leshanInstance.Reservations.Instances.PublicDnsName

Connecting to the server via SSH

You can use SSH, with the private key, to access the server directly:

$leshanStack = aws cloudformation describe-stacks --stack-name Lwm2mDemoServerStack | ConvertFrom-Json
$leshanInstance = aws ec2 describe-instances --instance-ids $leshanStack.Stacks[0].Outputs[0].OutputValue | ConvertFrom-Json
ssh -i ~/.ssh/leshan-demo-key.pem "ec2-user@$($leshanInstance.Reservations.Instances.NetworkInterfaces.Ipv6Addresses.Ipv6Address[1])"

Then run the Leshan server, from the remote shell:

nohup java -jar /home/ec2-user/leshan-server/leshan-server-demo.jar &

This will run the service in the background (use fg to recover it).

Viewing the Leshan web UI

The web portal is accessible via HTTPS, using the DNS name $leshanInstance.Reservations[0].Instances.PublicDnsName

You will be prompted with to enter a username ('iotadmin') and the web password that you configured.

Leshan web UI: banner

Download the Leshan demo client

You can use the Leshan demo client to test.

Install Java Runtime Environment if needed:

sudo apt install default-jre

Download the test client to a working folder:

cd ../temp
wget https://ci.eclipse.org/leshan/job/leshan/lastSuccessfulBuild/artifact/leshan-client-demo.jar

Configuring pre-shared key security

Generate the pre-shared key (PSK) ID and key, e.g.

$id = "urn:imei:3504577901234567"
$key = ((Get-Random -Max 0xff -Count 32|ForEach-Object ToString X2) -join '')
$id, $key

In the Leshan Web UI, go to Security > Add new client security configuration, and enter the following:

  • Client endpoint: urn:imei:3504577901234567 (as above)
  • Security mode: Pre-Shared Key
  • Identity: urn:imei:3504577901234567
  • Key: (as generated)

Click Create, and the endpoint will be added to the list.

Leshan web UI: client security configuration

Running the client

Run the demo client, passing in the address of the Azure Leshan server.

$leshanStack = aws cloudformation describe-stacks --stack-name Lwm2mDemoServerStack | ConvertFrom-Json
$leshanInstance = aws ec2 describe-instances --instance-ids $leshanStack.Stacks[0].Outputs[0].OutputValue | ConvertFrom-Json
$leshanInstance.Reservations[0].Instances.Ipv6Address
java -jar ./leshan-client-demo.jar -n $id -i $id -p $key -u "coaps://[$($leshanInstance.Reservations.Instances.NetworkInterfaces.Ipv6Addresses.Ipv6Address[1])]:5684"

In the web UI, you will be able to see the device connected, with the client address and security indicator. Note the IPv6 addresses being used (web UI and client), the padlock icon in the web UI, and the use of COAPS in the client console, with DTLS full handshake.

Leshan client connected in UI

Cloud to device messages

From the device screen you can operate functions like reading values from the client.

Leshan cloud read client object

Next steps

Once you have a secure LwM2M test server set up on the Internet, you can use it to test and validate devices. We will look to cover the device side in a future article.

Consider the Telstra Wireless Application Development Guidelines, particularly the considerations around IPv6 and security; and if you are looking to get devices Telstra Certified then these features are required: https://www.telstra.com.au/business-enterprise/products/internet-of-things/capabilities

In some cases existing devices may not support IPv6 -- the deployed LwM2M server is dual stack, so you can also access it via IPv4.

You may also have existing devices that don't support COAPS security. In these cases you would need to set up a private APN (Access Point Name) and private network to your service endpoint, to ensure that credentials are kept secure. For testing purposes you can also connect to the test server via unecrypted COAP.

LwM2M is a standardised protocol, with defined semantics that immediately allow any confirming device to communicate meaningful information. A large number of LwM2M objects are defined, covering a variety of scenarios.

You can also extend the protocol and create custom objects if needed, and can then load those object definitions into the system to activate them.

AI and copyright: Addressing an incongruity(4 min read)

In our rapidly evolving digital age, traditional concepts such as copyright are being rigorously tested. The advent of Artificial Intelligence (AI), in particular, has initiated a crucial debate on the validity and efficacy of existing copyright laws. This debate becomes particularly intense when we consider the striking difference in the treatment of photographs and AI-generated art under current copyright laws. This discrepancy calls into question our collective understanding of creativity, ownership, and the essence of copyright itself. Is it time for us to reevaluate the concept of copyright in the digital era?

Continue reading AI and copyright: Addressing an incongruity(4 min read)

Device Authentication with Nordic Thingy:91 and Azure IoT Hub(22 min read)

Security is an important topic for the Internet of Things, and there are several considerations to secure device identity. A good practice is to use secure protocols (such as TLS or DTLS) for transmitting any sensitive information over the network and to ensure that passwords and other sensitive information are securely stored.

This article will provide an example of using X.509 client certificates for connecting to Azure IoT, using the Nordic Thingy:91 platform. The certificates are securely loaded directly to the device, so they are not exposed in the device firmware.

Using certificates allows a hierarchy of trust to be established, allowing system owners to delegate certificate management to third parties while retaining control of the root trust.

The article also covers the usage of IPv6, and accessing IPv4 servers from the Telstra IoT network, running in IPv6-only mode and using NAT64.

Nordic Thingy:91 Cellular IoT Prototyping Platform, unboxed.

Continue reading Device Authentication with Nordic Thingy:91 and Azure IoT Hub(22 min read)

Dogs in the Vineyard – Conflict Process(2 min read)

I've been playing a few short sessions of Dogs in the Vineyard, so put together a one page step-by-step summary of the conflict process.

Note that the process does not include the detailed tables for fallout. You will need to refer to the main rules for that, or the D.O.G.S. generic version.

It is an interesting system, where a conflict is not a single roll of the dice, but an entire scene that you add (recurring) elements to as it progresses; it kind of reminds me a bit of the conflict resolution in Mouse Guard.

It is not just a "I do this", roll, done, type system, but one where resolving the conflict forms the narrative story line, as you bring in elements. The more narratively important an item is to you, the more dice you should assign it - a similar example from the Cortex Plus Firefly game is "Jayne's Hat". Assigning that item 2d10 (in DitV) would mean you want to narratively bring it into every conflict that you can.

I can't even think how to approach a statistical analysis of DitV, like I have done for other systems. Systems like Corex Prime are complex enough so that you can't readily calculate probabilities in your head — you know 3d6 is better than 2d6, and 2d10 is better than 2d8, but trying to compare (1d10 + 3d6) vs (4d8 + 1d4) is not something you can do off the cuff. But you can mathemtically calculate it.

Because DitV involves some foresight (you roll a little bit in advance before you pick what to use), it relies on some tactical skill at picking dice, not just probabilities. And because what you will roll depends heavily on the narrative direction the encounter goes, it is difficult to know what even will be relevant.

The resolution is also at the entire encounter level, not individual actions. e.g. it is easy to statistically determine if you will hit in Dungeons & Dragons, and what your average expected damage will be. But trying to determine who will win in a fight between a Rogue and Cleric vs three Orcs is a lot more complicated.

I will look at doing a longer analysis of the system in the future, but for now the conflict process summary is a start.

RPG Mechanics: Success at a cost(18 min read)

Tabletop roleplaying games are generally about shared storytelling, and stories are generally pretty boring if the story ends because the protagonists don't find the clue, don't get past the locked door, or don't survive a battle. Failure needs to continue the story by leading to something else interesting, being only a partial failure, or being success at a cost.

I will detail some of the concepts and approaches used by different systems, including several open source alternative roleplaying games, that can be incorporated into your game, even if the specific mechanics are not.

A related concept is the treatment of character death, which is usually the ultimate failure.

Some systems, like early Dungeons & Dragons, or revival games like Dungeon Crawl Classics, treat the adventuring group more like a wargaming troop, where when one character dies the next steps up to continue as the protagonist. Other modern systems have more explicit success at a cost mechanics or alternative ways to handle death.

The discussion is organised by concept, with examples given from different systems, including Fate, Dungeon World (Powered by the Apocalypse), Blades in the Dark, and Gumshoe.

Continue reading RPG Mechanics: Success at a cost(18 min read)

Running NAT64 in a dual stack network(5 min read)

Network address translation 6-to-4 (NAT64, RFC 6146) is a transition technology that can be used, in conjunction with DNS64 (domain name system 6-to-4, RFC 6147), to replace NAT44 in dual-stack networks, and allowing support of IPv6 only devices.

Dual stack is a common deployment solution for adding IPv6 for both consumer and corporate networks, although IPv6-only is becoming more common, with the typical guidance being "IPv6-Only Where You Can, Dual-Stack Where You Must"

Even if you are still stuck in dual stack, it still makes sense to use some of the IPv4 as a Service technologies, such as NAT64 and DNS64, which have the upside of allowing you to support IPv6 only devices, and no downside. As an additional benefit, you also get valuable experience in IPv6 systems.

The cost is that you need to have infrastructure that supports NAT64, either provided by your ISP, or from your own networking equipment/router. This is not as much an issue for DNS64, as public DNS64 is available, e.g. Google.

If your network supports it, look at implementing NAT64 + DNS64 today; if it does not, contact your equipment provider to find out when they will support this important technology for IPv6.

Network with IPv6 and dual stack devices using NAT64 to access an IPv4 server, with IPv4 devices using NAT44

Continue reading Running NAT64 in a dual stack network(5 min read)

Smart Buildings — Running an OpenThread Border Router(18 min read)

Thread is a mesh networking stack running on 6LoWPAN (IPv6 over Low-Power Wireless Personal Area Networks) over IEEE 802.15.4 radios. To connect to the broader network, a Thread Border Router is required, which acts as a gateway between the Thread mesh radio network and upstream networks.

Thread, especially when used with Matter, is an important development for home automation, however the technologies also have commercial applications. The initial commercial focus of Thread is for smart buildings.

The networking layer sits between the underlying physical network, and the application layers on top.

Thread layers: UDP, IP Routing, 6LowPAN, and cross-cutting Security/Commissioning, with non-Thread layers beow IEEE 802.15.4 MAC and IEEE 802.15.4 PHY, and non-Thread applications layer above

Matter is an application protocol for device automation that runs on top of Thread (and also WiFi), with Bluetooth used for device commissioning. Matter 1.0 was also released in October 2022 and is supported by major home automation vendors (Google, Amazon, Apple, and Samsung), but can also be used in commerical deployments.

When provisioning a Matter device to a Thread mesh, Bluetooth is used for the initial provisioning and sets up both the connection the the Thread mesh and registration in the Matter Hub. One important aspect of Matter is multi-admin, allowing one device to be controlled by multiple hubs.

The layered approach means Thread can be used by itself, providing mesh networking for smart buildings using other protocols, or in conjunction with Matter.

The article also looks at setting up a OpenThread Border Router for testing, and shows provisions a Matter test device to the Thread mesh.

Continue reading Smart Buildings — Running an OpenThread Border Router(18 min read)

M5Stack Atom NB-IoT device with secure MQTT over IPv6(20 min read)

M5Stack produce a suite of pilot-suitable modular IoT devices, including the Atom DTU NB-IoT. The NB-IoT DTU (Narrow Band Internet of Things - data transmission unit) comes in a small 64 24 29mm case with a DIN rail clip on mounting and support for RS-485 including 9-24V power (or USB-C power).

The kit base has a SIM7020G modem and the ESP32-based Atom Lite (which also supports WiFi) is included with a very resonable price. The device has built in MQTT, supports secure public certificate TLS connections, and supports IPv6.

While the physical unit is ready for pilot deployment (and the M5Stack website has several commerical deployment case studies), there is no pre-written firmware for the device, so some up front development is needed.

As well as reviewing the strengths and weaknesses of the device, I will also provide some sample code for a proof-of-concept using an Env III environment sensor to transmit temperature, humidity, and air pressure to an MQTT test server using MQTTS (with server certificates), over IPv6, over NB-IoT.

M5Stack Atom DTU NB-IoT with Telstra SIM card

Continue reading M5Stack Atom NB-IoT device with secure MQTT over IPv6(20 min read)