blob: 0b55f1d24b20bc952f7116d8b8d3198d0d4d26c5 [file] [log] [blame]
openapi: 3.0.1
info:
title: Eclipse Hono™ Device Registry API
description: |
This API defines how to manage *Tenants*, *Devices*, and *Credentials*.
It acts as a common basis which all Hono device registries should
implement.
## Required APIs
All operations, except the `tenants` resource are required. The tenant
management might be outside of the scope of the device registry and
managed by a higher level system. In this case all calls should simply
return `404`. However, if the `tenants` resource is implemented, then all
operations of it must be implemented.
## Security
This specification explicitly leaves out the part of authenticating and
authorizing users with the device registry. It is assumed that some form
of token exchange between the user agent and the backend service will
take place. Like for example HTTP basic authentication, or a bearer token.
## Code generation
This model is not optimized for generating code from it. Code generators
try to understand the model and then translate this into the require
programming language. Even if the there would be no bugs in the code
generators, that process would be already only be an approximation. So
this model does focus in the description of the API, and doesn't tweak
the specification in a way to please code generators.
contact:
name: Contact details
url: https://www.eclipse.org/hono/community/get-in-touch/
license:
name: EPL-2.0
url: https://www.eclipse.org/legal/epl-2.0/
version: 1.1.0
externalDocs:
description: Eclipse Hono™ web page
url: https://eclipse.org/hono
tags:
- name: tenants
description: Tenant Management (optional)
externalDocs:
description: Hono Multi-Tenancy
url: https://www.eclipse.org/hono/docs/concepts/tenancy/
- name: devices
description: Device registration
externalDocs:
description: Hono device identity
url: https://www.eclipse.org/hono/docs/concepts/device-identity/
- name: credentials
description: Device credentials
externalDocs:
description: Hono device identity
url: https://www.eclipse.org/hono/docs/concepts/device-identity/
servers:
- url: '{server}/v1'
variables:
server:
default: http://hono.eclipse.org:28080
security:
- BearerAuth: []
- BasicAuth: []
paths:
# Tenant API
/tenants:
post:
tags:
- tenants
summary: Create new tenant with auto-generated ID
operationId: createTenant
requestBody:
description: New tenant information
content:
application/json:
schema:
$ref: '#/components/schemas/Tenant'
required: false
responses:
201:
$ref: '#/components/responses/Created'
400:
$ref: '#/components/responses/MalformedRequest'
401:
$ref: '#/components/responses/Unauthorized'
403:
$ref: '#/components/responses/NotAllowed'
409:
description: |
Indicates that an existing tenant uses a certificate authority with the same Subject DN.
If the client has no read access to the conflicting tenant then `403` should be returned instead.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "Root Certificate Authority is already used by other tenant"
subject-dn: "CN=devices,OU=iot,O=ACME"
/tenants/{tenantId}:
parameters:
- $ref: '#/components/parameters/tenantId'
post:
tags:
- tenants
summary: Create new tenant
operationId: createTenantWithId
requestBody:
description: New tenant registration
content:
application/json:
schema:
$ref: '#/components/schemas/Tenant'
required: false
responses:
201:
$ref: '#/components/responses/Created'
400:
$ref: '#/components/responses/MalformedRequest'
401:
$ref: '#/components/responses/Unauthorized'
403:
$ref: '#/components/responses/NotAllowed'
409:
description: |
Indicates that tenant with the given identifier already exists or that an existing tenant uses
a certificate authority with the same Subject DN.
If the client has no read access to the conflicting tenant then `403` should be returned instead.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "tenant with given identifier already exists"
get:
tags:
- tenants
summary: Get tenant information
operationId: getTenant
responses:
200:
description: operation successful
content:
application/json:
schema:
$ref: '#/components/schemas/Tenant'
headers:
ETag:
description: Version of the resource
schema:
type: string
400:
$ref: '#/components/responses/MalformedRequest'
401:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
put:
tags:
- tenants
summary: Update tenant information
operationId: updateTenant
parameters:
- $ref: '#/components/parameters/resourceVersion'
requestBody:
description: Tenant information
content:
application/json:
schema:
$ref: '#/components/schemas/Tenant'
required: true
responses:
204:
$ref: '#/components/responses/Updated'
400:
$ref: '#/components/responses/MalformedRequest'
401:
$ref: '#/components/responses/Unauthorized'
403:
$ref: '#/components/responses/NotAllowed'
404:
$ref: '#/components/responses/NotFound'
409:
description: |
Indicates that an existing tenant uses a certificate authority with the same Subject DN.
If the client has no read access to the conflicting tenant then `403` should be returned instead.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: "Root Certificate Authority is already used by other tenant"
subject-dn: "CN=devices,OU=iot,O=ACME"
412:
$ref: '#/components/responses/ResourceVersionMismatch'
delete:
tags:
- tenants
summary: Delete tenant
operationId: deleteTenant
parameters:
- $ref: '#/components/parameters/resourceVersion'
responses:
204:
$ref: '#/components/responses/Deleted'
401:
$ref: '#/components/responses/Unauthorized'
403:
$ref: '#/components/responses/NotAllowed'
404:
$ref: '#/components/responses/NotFound'
412:
$ref: '#/components/responses/ResourceVersionMismatch'
# Device API
/devices/{tenantId}:
parameters:
- $ref: '#/components/parameters/tenantId'
post:
tags:
- devices
summary: Create new device registration with auto-generated ID
operationId: createDeviceRegistration
requestBody:
description: New device
content:
application/json:
schema:
$ref: '#/components/schemas/Device'
required: false
responses:
201:
$ref: '#/components/responses/Created'
400:
$ref: '#/components/responses/MalformedRequest'
401:
$ref: '#/components/responses/Unauthorized'
403:
$ref: '#/components/responses/NotAllowed'
/devices/{tenantId}/{deviceId}:
parameters:
- $ref: '#/components/parameters/tenantId'
- $ref: '#/components/parameters/deviceId'
post:
tags:
- devices
summary: Create new device registration
operationId: createDeviceRegistrationWithId
requestBody:
description: New device
content:
application/json:
schema:
$ref: '#/components/schemas/Device'
required: false
responses:
201:
$ref: '#/components/responses/Created'
400:
$ref: '#/components/responses/MalformedRequest'
401:
$ref: '#/components/responses/Unauthorized'
403:
$ref: '#/components/responses/NotAllowed'
409:
$ref: '#/components/responses/AlreadyExists'
get:
tags:
- devices
summary: Get device registration information
operationId: getRegistration
responses:
200:
description: operation successful
content:
application/json:
schema:
$ref: '#/components/schemas/Device'
headers:
ETag:
description: Version of the resource
schema:
type: string
400:
$ref: '#/components/responses/MalformedRequest'
401:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
put:
tags:
- devices
summary: Update existing device registration
operationId: updateRegistration
parameters:
- $ref: '#/components/parameters/resourceVersion'
requestBody:
description: Updated device registration
content:
application/json:
schema:
$ref: '#/components/schemas/Device'
required: true
responses:
204:
$ref: '#/components/responses/Updated'
400:
$ref: '#/components/responses/MalformedRequest'
401:
$ref: '#/components/responses/Unauthorized'
403:
$ref: '#/components/responses/NotAllowed'
404:
$ref: '#/components/responses/NotFound'
412:
$ref: '#/components/responses/ResourceVersionMismatch'
delete:
tags:
- devices
summary: Delete device registration
operationId: deleteRegistration
parameters:
- $ref: '#/components/parameters/resourceVersion'
responses:
204:
$ref: '#/components/responses/Deleted'
401:
$ref: '#/components/responses/Unauthorized'
403:
$ref: '#/components/responses/NotAllowed'
404:
$ref: '#/components/responses/NotFound'
412:
$ref: '#/components/responses/ResourceVersionMismatch'
/credentials/{tenantId}/{deviceId}:
parameters:
- $ref: '#/components/parameters/tenantId'
- $ref: '#/components/parameters/deviceId'
get:
tags:
- credentials
summary: Get credentials set of a device.
description: |
Get the credentials set of a device. As long as the device is
registered and the user has read access to it, this call should
never return "not found".
Depending on its implementation (or configuration), the device registry
can either return all credentials information including full secret details or
secret metadata along with the generated identifier (an `id` property).
The identifier can be used for the follow-up `update` operation).
operationId: getAllCredentials
responses:
200:
description: Operation successful
content:
application/json:
schema:
$ref: '#/components/schemas/CredentialsSet'
examples:
Credentials Metadata:
$ref: '#/components/examples/MetaPasswordExample'
headers:
ETag:
description: Version of the resource
schema:
type: string
400:
$ref: '#/components/responses/MalformedRequest'
401:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
put:
tags:
- credentials
summary: Update credentials set for registered device
description: If the device registry is handling full secret details, the updated credential set
will be an exact match of the provided content. If it is using secret metadata,
data will be merged in based on the secret identities.
operationId: setAllCredentials
parameters:
- $ref: '#/components/parameters/resourceVersion'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CredentialsSet'
examples:
Hashed Password:
$ref: '#/components/examples/HashedPasswordExample'
Plain Password:
$ref: '#/components/examples/PlainPasswordExample'
required: true
responses:
204:
$ref: '#/components/responses/Updated'
400:
$ref: '#/components/responses/MalformedRequest'
401:
$ref: '#/components/responses/Unauthorized'
403:
$ref: '#/components/responses/NotAllowed'
404:
$ref: '#/components/responses/NotFound'
412:
$ref: '#/components/responses/ResourceVersionMismatch'
components:
schemas:
# Common schema
Error:
type: object
additionalProperties: true
required:
- error
properties:
"error":
type: string
description: A human readable error message of what went wrong.
DefaultProperties:
type: object
additionalProperties: true
description: Defaults for properties defined on the tenant and device level.
Extensions:
type: object
additionalProperties: true
description: Allows arbitrary properties as extension to the ones
specified by the Hono API.
# Tenant schema
Tenant:
type: object
additionalProperties: false
properties:
"enabled":
type: boolean
default: true
"ext":
$ref: '#/components/schemas/Extensions'
"adapters":
type: array
description: |
A list of configuration options for certain types of protocol adapters.
If set then the array must not be empty.
Multiple entries for the same type are considered an error.
If not set, then all adapters are enabled using their respective
default configuration.
items:
$ref: '#/components/schemas/Adapter'
"defaults":
$ref: '#/components/schemas/DefaultProperties'
"minimum-message-size":
type: integer
default: 0
description: |
The minimum message size in bytes. If set, then reported size of
telemetry, event and command messages is calculated as the minimum multiple
of the configured value that is greater than or equal to the messages
payload size.
"resource-limits":
$ref: '#/components/schemas/ResourceLimit'
"tracing":
$ref: '#/components/schemas/TracingConfig'
"trusted-ca":
type: array
description: |
The set of root certificate authorities which are used for verifying the signature of
client certificates that devices use for authentication.
items:
$ref: '#/components/schemas/TrustedCA'
TrustedCA:
type: object
additionalProperties: false
properties:
"subject-dn":
type: string
description: |
The subject DN of the trusted root certificate in
the format defined by RFC 2253.
CAs of the *same* tenant may share the same subject DN, e.g.
allowing for the definition of overlapping validity periods.
However, CAs of *different* tenants must not share the same
subject DN in order to allow for the unique look up of a tenant by
the subject DN of one of its trusted CAs.
If the `cert` property is used to provide an X.509 certificate
then the subject DN is determined from the certificate and this
property is ignored.
Otherwise, i.e. if the `public-key` property is
used, this property is mandatory.
"public-key":
type: string
format: byte
description: |
The Base64 encoded binary DER encoding of the
trusted root certificate’s public key.
If the `cert` property is used to provide an
X.509 certificate then the public key is extracted
from the certificate and this property is ignored.
Either this property or `cert` must be set.
"algorithm":
type: string
description: |
The algorithm used for the public key of the CA.
If the `cert` property is used to provide an
X.509 certificate then the algorithm is determined
from the certificate and this property is ignored.
Otherwise, i.e. if the `public-key` property is
used, this property must be set to the algorithm
used, if other than the default.
default: RSA
example: EC
"not-before":
type: string
format: date-time
description: |
The point in time from which on the certificate authority
may be used for authenticating devices.
If the `cert` property is used to provide an
X.509 certificate then the point in time is
determined from the certificate and this property is ignored.
Otherwise, i.e. if the `public-key` property is
used, this property is mandatory.
"not-after":
type: string
format: date-time
description: |
The point in time until which the certificate authority
may be used for authenticating devices.
If the `cert` property is used to provide an
X.509 certificate then the point in time is
determined from the certificate and this property is ignored.
Otherwise, i.e. if the `public-key` property is
used, this property is mandatory.
"cert":
type: string
format: byte
description: |
The Base64 encoded binary DER encoding of the trusted X.509 root certificate.
This property can be used as a convenient alternative to
specifying the `public-key`, `not-before`, `not-after` and
`algorithm` properties explicitly. Implementors of this
API may choose to support this property only for uploading
a certificate but then extract all relevant data and store
it in the properties described above.
Either this property or `public-key` must be set.
"auto-provisioning-enabled":
type: boolean
default: false
description: |
Indicates whether this trusted certificate may be used for automatically provisioning devices.
If it is *true* and the feature is supported by the device registry, the device registry
automatically provisions unregistered devices that authenticate with a client certificate
issued by this CA (i.e. creates registration and credentials).
example:
subject-dn: "CN=devices,OU=iot,O=ACME"
public-key: "Tk9UIEEgUFVCTElDIEtFWQ=="
algorithm: "EC"
auto-provisioning-enabled: false
not-before: "2019-10-03T13:45:16+02:00"
not-after: "2021-10-03T00:00:00Z"
Adapter:
type: object
additionalProperties: true
required:
- type
properties:
"type":
type: string
"enabled":
type: boolean
default: false
"device-authentication-required":
type: boolean
default: true
"ext":
$ref: '#/components/schemas/Extensions'
ResourceLimit:
type: object
additionalProperties: false
properties:
"max-connections":
type: integer
default: -1
description: |
The maximum number of concurrent connections allowed from devices of this tenant.
A value of `-1` (the default) indicates that no limit is set.
"max-ttl":
type: integer
default: -1
description: |
The maximum time-to-live (in seconds) to use for events published by
devices of this tenant. Any default TTL value specified
at either the tenant or device level will be limited to
the max value specified here.
If this property is set to a value greater than -1 and no
default TTL is specified for a device, the max value will
be used for events published by the device.
A value of `-1` (the default) indicates that no limit is set.
Note that this property contains the TTL in seconds whereas
the AMQP 1.0 specification defines a message's ttl header
to use milliseconds.
"data-volume":
$ref: '#/components/schemas/DataVolume'
"connection-duration":
$ref: '#/components/schemas/ConnectionDuration'
"ext":
$ref: '#/components/schemas/Extensions'
TracingConfig:
type: object
additionalProperties: false
properties:
"sampling-mode":
type: string
description: |
Defines if and how often OpenTracing spans are being
sampled when processing messages for this tenant.
The value `default` indicates that the underyling tracing
system's default sampling mode should be used.
The value `all` indicates that every span created for
messages of the tenant will be sampled.
The value `none` indicates that no spans should be sampled
at all for the tenant.
The mode defined here may be overridden for specific
devices by means of the `sampling-mode-per-auth-id`
property.
default: default
enum:
- default
- all
- none
"sampling-mode-per-auth-id":
type: object
description: |
Defines if and how often OpenTracing spans are being
sampled when processing messages for specific devices
of this tenant.
This object contains a property for each device for which
specific behavior should be defined, using the device's
authentication identifier as the property name and
the device specific sampling mode as its value.
The value `default` indicates that the underyling tracing
system's default sampling mode should be used.
The value `all` indicates that every span created for
messages of the tenant will be sampled.
The value `none` indicates that no spans should be sampled
at all for the tenant.
The mode defined for a particular device has precedence
over the value defined by the `sampling-mode` property.
additionalProperties:
type: string
description: |
The property name is the device's 'authentication identifier.
default: default
enum:
- default
- all
- none
DataVolume:
type: object
additionalProperties: false
required:
- effective-since
properties:
"effective-since":
type: string
format: date-time
description: The date-time on which the data volume limit came into effect.
"max-bytes":
type: integer
default: -1
description: The maximum number of bytes to be allowed for a tenant for the
defined period.
A value of -1 (the default) indicates that no limit is set.
"period":
$ref: '#/components/schemas/Period'
ConnectionDuration:
type: object
additionalProperties: false
required:
- effective-since
properties:
"effective-since":
type: string
format: date-time
description: The date-time on which the connection duration limit came into effect.
"max-minutes":
type: integer
default: -1
description: The maximum number of minutes to be allowed for a tenant for the
defined period.
A value of -1 (the default) indicates that no limit is set.
"period":
$ref: '#/components/schemas/Period'
Period:
type: object
additionalProperties: false
required:
- mode
properties:
"mode":
type: string
description: The mode of the data usage caluclation. The supported modes by
the default resource limit checks implementation are "days"
and "monthly".
"no-of-days":
type: integer
description: The number of days for which the data usage is to be calculated
if mode is set to "days". Otherwise, this property is ignored.
# Devices schema
Device:
type: object
additionalProperties: false
properties:
"enabled":
type: boolean
default: true
"defaults":
$ref: '#/components/schemas/DefaultProperties'
"via":
type: array
items:
type: string
description: The device IDs of the gateways that are registered to act on behalf of this device. Note that "via" and "memberOf" must not be set at the same time.
"viaGroups":
type: array
items:
type: string
description: The IDs of the gateway groups that are registered to act on behalf of this device. Note that "viaGroups" and "memberOf" must not be set at the same time.
"memberOf":
type: array
items:
type: string
description: The IDs of the gateway groups that this device is a member of. Note that "via" and "memberOf" must not be set at the same time. The same applies for "viaGroups" and "memberOf" which must be set at the same time too. The reason is that Eclipse Hono does not support groups of gateway groups.
"ext":
$ref: '#/components/schemas/Extensions'
# Credentials
CredentialsSet:
type: array
description: A set of credentials. The entries in this list must be
unique by the composite key of `auth-id` and `type`.
items:
$ref: '#/components/schemas/TypedCredentials'
TypedCredentials:
additionalProperties: false
oneOf:
- $ref: '#/components/schemas/PasswordCredentials'
- $ref: '#/components/schemas/PSKCredentials'
- $ref: '#/components/schemas/X509CertificateCredentials'
discriminator:
propertyName: type
mapping:
"hashed-password": '#/components/schemas/PasswordCredentials'
"psk": '#/components/schemas/PSKCredentials'
"x509-cert": '#/components/schemas/X509CertificateCredentials'
CommonCredentials:
type: object
additionalProperties: false
required:
- auth-id
- type
properties:
"type":
type: string
"auth-id":
type: string
"enabled":
type: boolean
default: true
"ext":
$ref: '#/components/schemas/Extensions'
PasswordCredentials:
additionalProperties: false
allOf:
- $ref: '#/components/schemas/CommonCredentials'
- type: object
additionalProperties: false
properties:
"secrets":
type: array
items:
$ref: '#/components/schemas/PasswordSecret'
PSKCredentials:
additionalProperties: false
allOf:
- $ref: '#/components/schemas/CommonCredentials'
- type: object
additionalProperties: false
properties:
"secrets":
type: array
items:
$ref: '#/components/schemas/PSKSecret'
X509CertificateCredentials:
additionalProperties: false
allOf:
- $ref: '#/components/schemas/CommonCredentials'
- type: object
additionalProperties: false
properties:
"secrets":
type: array
items:
$ref: '#/components/schemas/X509CertificateSecret'
CommonSecret:
type: object
additionalProperties: false
properties:
"id":
type: string
description: The device registry can assign an identity to the secret.
This value can be used to update secrets based on their metadata.
"enabled":
type: boolean
default: true
"not-before":
type: string
format: date-time
"not-after":
type: string
format: date-time
"comment":
type: string
X509CertificateSecret:
additionalProperties: false
allOf:
- $ref: '#/components/schemas/CommonSecret'
PasswordSecret:
description: |
Password based secret definition <br>
__NOTE__: Defining password secrets with user provided password hash, function and salt is deprecated and will be removed
in the upcoming versions. You should use `pwd-plain` property only going forward.
additionalProperties: false
allOf:
- $ref: '#/components/schemas/CommonSecret'
- type: object
additionalProperties: false
properties:
"hash-function":
type: string
example: bcrypt
description: The name of the hash function used to create the password hash (defined in `pwd-hash` property).
If the password is defined using a `pwd-plain` property, this value will be ignored by the device registry.
This property should be empty when returning passwords from the device registry using only secret metadata.
In this case the id field must be set instead.
"pwd-hash":
type: string
format: byte
description: The password hash created using the `hash-function` and optional `salt` values.
If the password is defined using a `pwd-plain` property, this value will be ignored by the device registry.
This property should be empty when returning passwords from the device registry using only secret metadata.
In this case the id field must be set instead.
"salt":
type: string
format: byte
description: The Base64 encoding of the salt used in the password hash (defined in the `pwd-hash` property).
If the password is defined using a `pwd-plain` property, this value will be ignored by the device registry.
This property should be empty when returning passwords from the device registry using only secret metadata.
In this case the id field must be set instead.
"pwd-plain":
type: string
format: byte
description: The clear text value of the password to be hashed by the device registry.
If this property is specified, the device registry will ignore user-provided hash properties (`hash-function`, `pwd-hash` and `salt`).
This property must never be stored by the device registry.
This property must be empty when returning passwords from the device registry.
PSKSecret:
additionalProperties: false
allOf:
- $ref: '#/components/schemas/CommonSecret'
- type: object
additionalProperties: false
required:
- key
properties:
"key":
type: string
format: byte
parameters:
resourceVersion:
name: If-Match
in: header
description: The expected resource version
required: false
schema:
type: string
tenantId:
name: tenantId
in: path
description: The ID of the tenant
required: true
schema:
type: string
example: DEFAULT_TENANT
deviceId:
name: deviceId
in: path
description: The ID of the device
required: true
schema:
type: string
example: 4711
authId:
name: authId
in: path
description: The authentication ID of the device
required: true
schema:
type: string
example: sensor1
type:
name: type
in: path
description: The credentials type
required: true
schema:
type: string
example: sha-256
responses:
Unauthorized:
description: Authentication credentials are required, but missing.
headers:
"WWW-Authenticate":
schema:
type: string
Created:
description: Object created.
headers:
Location:
description: URL to the resource
schema:
type: string
format: uri
ETag:
description: The new version of the resource
schema:
type: string
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- id
properties:
id:
type: string
description: The ID of the created object
Updated:
description: Object updated.
headers:
ETag:
description: The new version of the resource
schema:
type: string
Deleted:
description: Object deleted.
NotFound:
description: |
Object not found. This may also be returned for some operations
if the user misses read access for the object.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
NotAllowed:
description: |
Operation not allowed. If the user does not have read access
for this object, then `404` will be returned instead.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
MalformedRequest:
description: Malformed request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
AlreadyExists:
description: |
Object already exists. If the user has no read access for
the existing object, then `403` should be returned instead.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
ResourceVersionMismatch:
description: |
Expected resource version does not match current.
This can only happen when the request header `If-Match`
was set.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
securitySchemes:
BearerAuth:
type: http
scheme: bearer
BasicAuth:
type: http
scheme: basic
examples:
HashedPasswordExample:
value:
[{
auth-id: sensor1,
type: hashed-password,
secrets: [{
"not-after": "2027-12-24T19:00:00Z",
"pwd-hash": "AQIDBAUGBwg=",
"salt": "Mq7wFw==",
"hash-function": "sha-512"
}]
}]
PlainPasswordExample:
value:
[{
auth-id: sensor1,
type: hashed-password,
secrets: [{
"id": "349556ea-4902-47c7-beb0-1009ab693fb4",
"not-after": "2027-12-24T19:00:00Z",
"pwd-plain": "hono-secret"
}]
}]
MetaPasswordExample:
value:
auth-id: sensor1
type: hashed-password
secrets: [{
"id": "349556ea-4902-47c7-beb0-1009ab693fb4",
"not-after": "2027-12-24T19:00:00Z",
}]