---
title: Quickstart
path: quickstart
status: published
---

# Quickstart

This walks you from an empty directory to a personal-data field that you write,
read back, decrypt through a subject access request, and then cryptographically
erase — five copy-pasteable steps. Everything runs against a standalone
`scailog-server`: one process, one SQLite file, no other services. You need
Python 3.11+.

## 1. Install and run the standalone server

Install the server and CLI, initialize a data directory, then boot in standalone
mode. `--init` mints and prints the **bootstrap key once** — it is a full-access
API key, so copy it now; it is never shown again.

```bash
python3 -m venv .venv && . .venv/bin/activate
pip install 'scailog[all]'          # server + agent + cli

# Initialize keys and print the bootstrap key (shown once).
scailog-server --init --data-dir ./data
# => a long token — copy it.
```

Now start the server. `SCAILOG_STANDALONE=true` embeds the per-host agent so the
server opens the local ingest socket itself; `SCAILOG_KEYSTORE_PASSPHRASE`
unlocks the on-disk keystore. `SCAILOG_SOCKET` points the embedded agent (and,
in step 2, the SDK) at a socket path you can write to without root.

```bash
export SCAILOG_KEYSTORE_PASSPHRASE=dev
export SCAILOG_STANDALONE=true
export SCAILOG_SOCKET="$PWD/data/agent.sock"
scailog-server --standalone --data-dir ./data --port 8080
```

Leave that running. In a second terminal, point the CLI at the server with the
bootstrap key you copied:

```bash
export SCAILOG_API_KEY='<paste the bootstrap key>'
scailog status          # should print server status JSON
```

## 2. Emit a log with a PI field and a subject

Install the SDK in your service and emit one entry. You wrap the PII value with
`pi(value, pi_type)` and pass a `subject=` — PI without a subject is rejected,
because the subject is who the per-subject key belongs to.

```bash
pip install scailog     # the SDK is pure-stdlib, zero third-party deps
export SCAILOG_SOCKET="$PWD/data/agent.sock"   # same socket the server opened
export SCAILOG_SERVICE=checkout
export SCAILOG_TENANT=default
```

```python
# emit.py
from scailog import log, pi

log.info(
    "SCAI-DEMO-USER-LOGIN-001",           # your call_site_id (constant + unique)
    "user logged in",                      # message — must stay PI-free
    subject="usr_8842",                    # the data subject this PI belongs to
    fields={
        "method": "oidc",                                    # plain field
        "email": pi("jan@example.nl", "direct_identifier"),  # PI field, encrypted
    },
)
log.flush()   # the emitter is async; flush before the process exits
```

```bash
python emit.py
```

The `email` field is encrypted under `usr_8842`'s per-subject DEK before it hits
storage. The `message` and the plain `method` field are stored in the clear and
are full-text searchable; the encrypted PI never enters the search index.

## 3. Query it back

Read the entry back with the CLI. Query never decrypts PI — it shows which PI
fields an entry carries (by name and type), not their values.

```bash
scailog query --service checkout --subject usr_8842
```

You get a row for your entry: the `ts`, `level`, `service`, `call_site_id`, the
`message` ("user logged in"), and a `pi` column listing `email`. The value stays
encrypted; to read it you run a DSAR.

## 4. Run a DSAR for the subject

A Data Subject Access Request (GDPR Article 15) collects every entry for a
subject and **decrypts their PI** into a signed report. This needs the `dpo`
capability, which the bootstrap key has.

```bash
scailog dsar usr_8842 --out dsar.json
```

Open `dsar.json`. It contains `entry_count`, the `call_sites` involved, and an
`entries` array. Your entry's `pi_values` now shows the decrypted value:

```json
{
  "type": "dsar",
  "subject": "usr_8842",
  "entry_count": 1,
  "entries": [
    {
      "call_site_id": "SCAI-DEMO-USER-LOGIN-001",
      "message": "user logged in",
      "pi_values": { "email": "jan@example.nl" },
      "pi_categories_collected": { "email": "direct_identifier:encrypt" }
    }
  ],
  "signature": { "key_id": "…", "public_key": "…", "sig": "…" }
}
```

## 5. Erase the subject and confirm it is gone

Erasure (Article 17) destroys the subject's DEK. Because the PI was only ever
stored encrypted under that key, destroying it makes the PI unrecoverable — no
row-by-row scrubbing required.

```bash
scailog erase usr_8842 --confirm
# => erased — 1 entries tombstoned, receipt 01J…, absolute_after 2026-…Z
```

Run the DSAR again. The entry is still present for audit, but its PI can no
longer be decrypted — `pi_values` comes back empty and the report is flagged
`subject_erased: true`:

```bash
scailog dsar usr_8842
```

```json
{
  "subject": "usr_8842",
  "subject_erased": true,
  "entries": [
    { "call_site_id": "SCAI-DEMO-USER-LOGIN-001", "pi_values": {}, "tombstoned": true }
  ]
}
```

That is the whole loop: written, searched, decrypted on lawful request, and
cryptographically erased — the DEK is destroyed, so the erasure holds everywhere
that PI was ever written. Next, read the [Guides](/docs/scailog/guides) for the
full DSAR, erasure, and policy workflows.
