Security & compliance
This page is the reference for CORE-M’s account-security, access-control, and compliance controls. For request-level authentication (JWT vs API key, headers, error codes) see API overview; for how tenants are isolated see Auth & multi-tenancy.
Multi-factor authentication
Section titled “Multi-factor authentication”MFA is tenant-configurable, using TOTP plus single-use recovery codes. Factors
are stored encrypted in the Aerospike sessions namespace, mfa set, keyed
{tenant_id}:{user_id}.
| Action | Behaviour |
|---|---|
| Enroll TOTP | User confirms a valid code; the encrypted secret is stored; recovery codes are generated and returned exactly once; audit mfa.enrolled is written |
| Login (MFA required) | Password auth alone does not issue a token; the login response demands an MFA challenge; the access token is issued only after a valid TOTP code |
| Use a recovery code | Login succeeds, the code is invalidated, and audit mfa.recovery_code.used is written |
Password and session policy
Section titled “Password and session policy”Tenants configure password strength, session lifetime, and refresh-token rotation; users can list and revoke their own sessions.
| Control | Behaviour |
|---|---|
| Password policy | Weak passwords are rejected with INVALID_ARGUMENT; the response lists the failed rules without echoing the password |
| Refresh rotation | Each refresh issues a new token pair and invalidates the old refresh token |
| Revoke a session | The session’s refresh token is invalidated; its access token fails after the configured revocation-propagation delay; audit session.revoked is written |
| Revoke all other sessions | All sessions except the current one are invalidated |
Scoped API tokens and service accounts
Section titled “Scoped API tokens and service accounts”Scoped API tokens and service accounts are separate from device-provisioning
keys. Tokens live in the Aerospike sessions namespace, api_tokens set, keyed
{tenant_id}:{token_id}. The raw token value is returned exactly once; only the
hash, prefix, scopes, and timestamps are stored.
A token carries:
| Attribute | Meaning |
|---|---|
subject_type | user or service_account |
scopes | The permission set the token grants (table below) |
expires_at | Expiry timestamp |
ip_allowlist | Optional source-CIDR restriction |
prefix | Non-secret prefix shown in listings and audit |
created_at / last_used_at | Lifecycle timestamps |
Scopes
Section titled “Scopes”| Scope | Grants |
|---|---|
devices:read | Read device registry |
devices:write | Create / update / delete devices |
telemetry:ingest | Submit telemetry |
telemetry:read | Query telemetry and snapshots |
rules:write | Manage rules |
rpc:execute | Issue server-to-device RPCs |
exports:create | Create export jobs |
anchoring:read | Read anchor batches and proofs |
| Scenario | Outcome |
|---|---|
Token lacks the scope for a call (e.g. telemetry:read token calls CreateDevice) | PERMISSION_DENIED; audit api_token.denied records missing_scope="devices:write" |
| Rotate a service-account token | A new token is created; the old one is scheduled to expire after overlap_seconds; both prefixes are visible during the overlap window |
IP allowlists and network controls
Section titled “IP allowlists and network controls”Allowlists apply at both the tenant level (UI, API, protocol ingestion) and the individual token level.
| Scenario | Outcome |
|---|---|
| Token request from outside its CIDR allowlist | PERMISSION_DENIED; no downstream handler runs |
| Login from an IP excluded by the tenant UI allowlist | Rejected before password verification; audit login.denied_ip is written |
Audit log
Section titled “Audit log”Every security, admin, data-governance, command-execution, rule-deployment,
profile, dashboard, and anchoring-policy action writes an immutable audit
event. Events are stored in the Aerospike sessions namespace, audit set,
keyed {tenant_id}:{event_id}, and may be copied to long-term storage.
Fields
Section titled “Fields”| Field | Meaning |
|---|---|
event_id | Unique event ID |
tenant_id | Owning tenant |
actor_type / actor_id | Who acted (user or service account) |
action | What happened, e.g. device.deleted, retention.policy.updated |
target_type / target_id | The affected resource |
result / reason | Outcome and, when denied, why |
source_ip / user_agent | Request origin |
trace_id | Correlates to the distributed trace |
created_at | Event timestamp |
before_hash / after_hash | Integrity hashes around the change |
redacted_details_json | Detail payload with secrets removed |
Search and export
Section titled “Search and export”| Capability | Behaviour |
|---|---|
| Search | Filter by time range, actor, and action; results are paginated and ordered descending by created_at |
| Redaction | Sensitive values are never recorded — a token rotation logs only token_id, prefix, scopes, and expiration, never the raw token |
| Export | Audit events can be exported with a manifest (see Export jobs) |
Compliance export and data deletion
Section titled “Compliance export and data deletion”CORE-M supports data-subject workflows with explicit safeguards for immutable proofs.
| Workflow | Behaviour |
|---|---|
| User data export | Bundle contains the user’s profile, sessions, audit events involving them, dashboard preferences, and notification preferences; the download URL has an expires_at |
| Data deletion (device with final proofs) | Operational raw telemetry is deleted or redacted per policy; proof records remain; verification responses indicate the raw data is not retained; audit compliance.delete.completed records the preserved-proof count |
Secrets and custody
Section titled “Secrets and custody”Custody controls cover the secrets you configure: SSO client secrets, protocol credentials, connector secrets, and notification-provider secrets.
| Secret | Custody behaviour |
|---|---|
| Connector secret (e.g. webhook bearer token) | Stored in the configured secret backend; the rule-chain config keeps only a reference; API responses never return the value |
| Protocol / SSO credential | Stored hashed or as a backend reference; raw values are never returned by the API |
The platform prefers KMS-backed references over raw key material wherever a secret backend is available.