# Security Center

The Security Center is one page in the kosa8 Cloud dashboard (**Access → Security Center** at app.kosa8.com), the same thing in the CLI (`kosa8 cloud security`, `agents`, `freeze`, `siem`, `evidence`) and a **Cloud Security** panel in the desktop app's Account view. It reads your organization's own settings, API keys and [audit log](https://docs.kosa8.com/guides/audit/). It does not certify anything: the checklist is a self-check for you to act on, and the evidence file is your own record for your own review or your auditor's.

## Posture checklist

Each check passes, fails, or does not apply, and names the dashboard page that changes it. The score is the number that pass out of the number that apply, for example `7 / 12`. There is no letter grade.

| Check | Passes when |
|---|---|
| Single sign-on is connected | An OIDC connection is set up (Team & SSO). |
| Dashboard sign-in requires SSO | “Require SSO for the dashboard” is on. |
| An email domain is verified | At least one SSO domain is verified by its DNS TXT record. |
| A named person is an owner | At least one member has the owner role, so the org is not run only from a shared admin key. |
| Few people hold owner or admin | Owners plus admins are at most half the members, or at most two. Does not apply with no members. |
| At most 2 live admin keys | No more than two unrevoked, unexpired API keys have the `admin` scope. |
| No key older than 90 days | Every live key was made (or rotated) in the last 90 days. |
| No key unused for 30 days | Every live key was used, or made, in the last 30 days. |
| Non-admin keys have an end date | Every live key without `admin` has an expiry. |
| A monthly spending cap is set | Settings has a spending cap. |
| The audit log's hash chain verifies | `GET /v1/audit/verify` finds no edited, removed or reordered event. |
| The audit log left kosa8 in the last 30 days | An NDJSON export or an evidence export in the last 30 days, or a working SIEM destination. |
| The audit log streams to a SIEM | At least one SIEM destination whose last delivery succeeded. |

Not checked, and said so on the page: kosa8 Cloud has no built-in multi-factor sign-in (require SSO and use your identity provider's MFA), and no org-wide egress policy setting yet.

```
curl -s https://api.kosa8.com/v1/security/posture -H "Authorization: Bearer $KOSA8_API_KEY"
# {"score":{"passed":7,"total":12},"frozen":false,"items":[{"id":"sso_connected","area":"Identity",
#   "title":"Single sign-on is connected","ok":false,"detail":"…","fix":{"tab":"team","label":"Set up SSO"}},…]}
```

Reading it needs the owner or admin role, or a key with `audit:read`.

## Agent inventory

Every API key (an agent's, a CI job's or a person's) and every member, with its scopes or role, its state (`active`, `suspended`, `expired`, `revoked`), when it was last used, and the sandboxes and functions it made. Who made what comes from the audit log's successful `sandbox.create`, `sandbox.fork` and `function.deploy` events; anything made before the audit log, or by a key revoked more than 30 days ago, is listed as unattributed. Revoke ends a key at once through the usual `DELETE /v1/api-keys/{id}`.

```
$ kosa8 cloud agents
$ kosa8 cloud agents --revoke key_…
```

## Kill switch: freeze the organization

An owner or admin can freeze the organization, for example when a key may have leaked or an agent is doing something it should not. You type the organization's name to confirm (the CLI asks for it unless you pass `--yes`). A freeze:

- refuses new sandboxes, commands, wakes, forks, file writes, function calls and deploys, new keys and key rotation, with `423` and `{"error":"org_frozen"}`, scheduled and web-endpoint function calls included;

- puts every running sandbox to sleep through the normal sleep path (state kept, nothing deleted). A host that does not answer is retried by the minute tick until it does;

- with `suspend_keys`, suspends every live API key except the one making the call. A suspended key answers `401 key_suspended`. Members' dashboard sessions keep working, so someone can always unfreeze.

While frozen, reads and incident response still work: revoking keys, adding, demoting or removing members, changing SSO and settings, deleting or sleeping sandboxes, deleting functions and webhooks, and the Security Center itself. Function invocations already running when the freeze lands are not cut short.

Unfreezing restores the keys the freeze suspended, unless you ask it not to. Sandboxes stay asleep until woken; their next command wakes them. Both actions are in the audit log as `security.freeze` and `security.unfreeze`, with the reason, how many keys were suspended or restored and how many sandboxes were put to sleep; every refused call is recorded too.

```
curl -s -X POST https://api.kosa8.com/v1/security/freeze -H "Authorization: Bearer $KOSA8_API_KEY" \
  -H 'Content-Type: application/json' -d '{"confirm":"Acme Inc","reason":"key in a public repo","suspend_keys":true}'
curl -s -X POST https://api.kosa8.com/v1/security/unfreeze -H "Authorization: Bearer $KOSA8_API_KEY" \
  -H 'Content-Type: application/json' -d '{"confirm":"Acme Inc"}'   # "restore_keys":false keeps them suspended
```

```
$ kosa8 cloud freeze --reason "key in a public repo" --suspend-keys
$ kosa8 cloud unfreeze
```

## SIEM streaming

Every audit event, in order, to up to five destinations per organization. The minute tick sends the events after each destination's cursor, up to 100 per request and 500 per minute, and moves the cursor only when the destination answers `2xx`. A failure is retried after 1, 5 and 15 minutes, then hourly, and the stream resumes where it stopped: no event is skipped. Redirects are not followed, and URLs must be public `https`. Owners and admins manage destinations.

| Kind | Request |
|---|---|
| `webhook` | `POST` of `{"org_id","destination_id","events":[…]}` with `Kosa8-Signature: t=<unix s>,v1=<hex HMAC-SHA256(secret, "<t>.<body>")>`, as the [event webhooks](https://docs.kosa8.com/guides/cloud-webhooks/) sign. The `whsec_…` secret is shown once. |
| `splunk_hec` | Your HEC URL with `Authorization: Splunk <token>`; one `{"time","host","source","sourcetype","index"?,"event"}` object per line. `sourcetype` defaults to `ocsf:api_activity`. |
| `datadog` | `https://http-intake.logs.<site>/api/v2/logs` with `DD-API-KEY`; a JSON array of `{"ddsource":"kosa8","service":"kosa8-cloud","message","ocsf"}`. Sites: datadoghq.com, us3, us5, datadoghq.eu, ap1, ap2, ddog-gov.com. |

Tokens and API keys are stored encrypted and never returned by the API. Each event is OCSF API Activity (`class_uid` 6003, `category_uid` 6): `activity_id` from the action's verb (1 create, 2 read, 3 update, 4 delete, 99 other), `status_id` 2 with `status_code` for a refused call, `time`, `actor.user` (a member, an API key or the system), `api.operation` (the audit action, e.g. `sandbox.exec`), `resources`, and `metadata.uid` `org:seq`. kosa8 Cloud does not record client IP addresses, so `src_endpoint` carries only the channel (`key`, `session`, `sso`, `system`). The original event, its detail and its chain hashes are in `unmapped`.

```
curl -s -X POST https://api.kosa8.com/v1/security/siem -H "Authorization: Bearer $KOSA8_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"kind":"splunk_hec","url":"https://splunk.example.com:8088/services/collector/event","token":"…","index":"security"}'
# start: "beginning" (default, sends the whole log first) or "now"
curl -s -X POST https://api.kosa8.com/v1/security/siem/siem_…/test -H "Authorization: Bearer $KOSA8_API_KEY"
```

```
$ KOSA8_SIEM_TOKEN=… kosa8 cloud siem add --kind datadog --site datadoghq.eu
$ kosa8 cloud siem ls
$ kosa8 cloud siem test siem_…
```

## Evidence export

**Download evidence** (or `kosa8 cloud evidence export`, or `GET /v1/security/evidence?since=&until=`) saves one JSON file: the posture checklist, the audit chain's verification, and the audit events in the range (default the last 90 days, at most 20,000 events, with `truncated` set past that).

```
{"content":{"format":"kosa8.evidence.v1","statement":"…","org":{…},"range":{…},"posture":{…},
  "audit":{"chain":{"ok":true,…},"count":412,"truncated":false,"events":[…]}},"sha256":"9f2c…"}
```

`sha256` is the SHA-256 of `content` as served: canonical JSON, object keys sorted and no spaces, the same form the audit chain hashes. The CLI checks it before saving. To check it yourself:

```
python3 -c 'import json,hashlib,sys; f=json.load(open(sys.argv[1]));
c=json.dumps(f["content"],sort_keys=True,separators=(",",":"),ensure_ascii=False);
print(hashlib.sha256(c.encode()).hexdigest()==f["sha256"])' kosa8-evidence-….json
```

Each export is recorded in the audit log as `security.evidence_export`, with its hash. The file is generated by kosa8 Cloud from your organization's own data; it is not a certification, an attestation or an audit opinion.

## API

| Route | Scope |
|---|---|
| `GET /v1/security/posture` | `audit:read` |
| `GET /v1/security/inventory` | `audit:read` |
| `GET /v1/security/evidence` | `audit:read` |
| `GET /v1/security/freeze` | any key of the org |
| `POST /v1/security/freeze`, `/unfreeze` | `admin` (owners and admins) |
| `GET`, `POST /v1/security/siem`, `DELETE /v1/security/siem/{id}`, `POST …/{id}/test` | `admin` |

---
Source: https://docs.kosa8.com/guides/cloud-security/
