IOTA snapshot import v1
Status and lifetime
Section titled “Status and lifetime”iota-snapshot-import-v1 is an implemented but deliberately temporary V1
migration contract. It provides one manually triggered snapshot import and is
not a synchronization service, background job, scheduled task, or permanent
integration.
The feature must be removed immediately after V1 acceptance. Imported device types and devices remain normal SENS records. Historical audit events remain under the normal audit-retention policy.
Public HTTP contract
Section titled “Public HTTP contract”POST /v1/tenants/{tenantNumber}/imports/iota-snapshot accepts:
{ "baseUrl": "https://commonapi-gesundheit-burgenland.xaas-a0a0.cloud/api", "username": "entered-for-this-run", "password": "entered-for-this-run"}Only a current platform_admin session may call it. The path tenant is the
explicit destination and must exist with active status. Query strings and
unknown body fields are rejected. Success and error responses use
Cache-Control: no-store.
The base URL must use HTTPS, contain no URL credentials, port, query, or
fragment, have path /api, and match the host pattern
commonapi-<slug>.xaas-a0a0.cloud. Native HTTP redirects are disabled.
The response contains the destination tenant ID, completion timestamp, raw source counts, created/existing/skipped counts for each entity kind, and at most 200 safe issues. Stable issue codes are:
invalid_device_type_codeinvalid_dev_euimissing_device_typeduplicate_source_dev_euidevice_exists_in_other_tenantexisting_device_mismatchdisabled_existing_device_typeasset_excludedincomplete_source_record
IOTA authentication, availability, timeout, JSON, and response-shape failures
return only a safe DEPENDENCY_FAILURE; upstream bodies and request URLs are
not exposed.
IOTA source calls
Section titled “IOTA source calls”The HTTP adapter uses the locally modeled subset of the old IOTA contract:
POST /Authenticationwith query fieldsusername,password, andgenerateRefreshToken=false.GET /Tenantto enumerate every visible source tenant.GET /DeviceType?tenantId=…for every tenant.GET /Device?tenantId=…for every tenant.POST /Connection?tenantId=…with an empty filter for every tenant, used only to identify devices connected to excluded asset pseudo-devices.
Read calls use the returned access token as Bearer authentication. The token is
sent only to the validated original origin. Calls are sequentially paginated
with page size 100 and stable ID ordering. Each call has a 15-second timeout and
the whole source load has a 60-second deadline. Hard limits are 100 tenants,
1,000 device types, 1,000 devices, and 5,000 connections. Exceeding a limit
fails before any write.
IOTA 204 No Content responses from collection endpoints represent an empty
collection and do not fail the snapshot.
The generated contract and implementation in the retired sens-admin
repository are not runtime dependencies and were not copied. Only the required
response fields are validated locally.
Mapping
Section titled “Mapping”| IOTA field | SENS field | Rule |
|---|---|---|
Device Type code | device_types.key | Preserve an already valid key; otherwise derive a lowercase readable slug plus SHA-256 hash. |
Device Type code | device_types.name | Trim and preserve the original human-readable value. |
Device edid | devices.dev_eui | Trim, uppercase, require exactly 16 hexadecimal characters. |
Device serial | devices.name | Trim; use normalized DevEUI when empty. |
Device typeId | devices.device_type_id | Resolve within the source tenant to type code, then to the derived SENS type key. |
| Other source fields | — | Status, subtenant, attributes, and metadata are ignored. |
New records use active, metadata {}, and no device organization assignment.
The trimmed source type code is the durable import identity. Identical codes
from several source tenants merge into one destination type. Invalid-format
codes receive a deterministic key whose eight-character hash suffix prevents
different source codes with the same readable slug from colliding. Empty or
overlong source codes remain invalid and are skipped.
Asset exclusion follows the established IOTA type-code convention: a trimmed
code starting with asset or containing _asset followed by a separator is an
asset type. These types and their pseudo-devices are excluded. Connections are
tenant-scoped; when either endpoint of a connection is an asset pseudo-device,
the device at the other endpoint is excluded as being inside that asset. The
connection direction does not affect the decision. Unrelated device
connections do not exclude either endpoint. Exclusions are reported with
asset_excluded and happen before DevEUI validation.
Create-only and atomicity contract
Section titled “Create-only and atomicity contract”The complete source snapshot is loaded and normalized before the write transaction. The service verifies the destination, then locks its tenant row in the transaction so two imports for the same tenant serialize.
Missing types are created. Existing active types are reused unchanged. Existing disabled types are not reactivated, and dependent source devices are skipped. Devices are checked globally by normalized DevEUI. A device in another tenant is never moved. A device already in the destination remains unchanged; name or type differences produce an issue. Identical source rows for the same DevEUI deduplicate; contradictory rows for that DevEUI are all skipped.
Expected source problems are removed before persistence. Every remaining valid
type and device, its device_type.created or device.created audit event, and
the final iota_snapshot_import.completed summary event commit together. A
database, constraint, or audit failure rolls the whole valid subset back.
Secret, SSRF, logging, and audit boundary
Section titled “Secret, SSRF, logging, and audit boundary”Base URL, username, and password are form inputs for one request. There are no IOTA environment variables, Helm values, Kubernetes Secrets, database columns, cookies, or browser-storage entries. The password is cleared from component state after success or failure.
The constrained host pattern, HTTPS-only scheme, same-origin token use, disabled
redirects, and bounded calls prevent the route from acting as a general-purpose
server-side request primitive. Operators must still allow outbound DNS and
HTTPS from platform-api to the selected matching host.
Request bodies are excluded from platform logs and audits. Operational logs contain only correlation ID, destination tenant ID, operation, outcome, and aggregate counters. Prometheus labels never include tenant, URL, username, or DevEUI. Entity audit metadata contains counts only, never credentials or source records.
Metrics and dependency behavior
Section titled “Metrics and dependency behavior”The optional manual source is not a health or readiness dependency. Its metrics are:
sens_iota_snapshot_import_runs_total{outcome}sens_iota_snapshot_import_duration_secondssens_iota_snapshot_import_entities_total{entity,outcome}
The normal service_name and environment labels are also present. Outcomes
and entity labels are fixed low-cardinality values.
Mandatory removal after V1 acceptance
Section titled “Mandatory removal after V1 acceptance”- Remove the IOTA route, source adapter, import service, DTOs, runtime schemas, and metrics.
- Remove the IOTA form and result workflow from the Admin page.
- Remove the Admin page and sidebar group only if no other platform-admin function uses them.
- Remove the OpenAPI, API, Platform, contract, operations, and test material that describes this feature.
- Remove any no-longer-required DNS or HTTPS egress allowance.
- Retire or rotate the IOTA account outside this repository.
- Keep the migrated device types and devices.
- Keep historical audit events under normal retention.