Skip to content

Quickstart

Run the hello-sluice example and send your first query through Sluice.

These docs describe the main branch, so you build Sluice from source. You need Git, make, Go 1.26 or later, a C compiler, python3 and curl.

1. Build and start

git clone https://github.com/bino-bi/sluice.git
cd sluice
make build
export PATH="$PWD/bin:$PATH"
sluice version

Start the example:

examples/hello-sluice/serve.sh

The script creates the SQLite database data/shop.db with the tables customers, orders and admin_users for three tenants. Then it sets demo secrets and starts Sluice. On the first start, Sluice downloads the DuckDB extension for SQLite. Sluice listens on http://localhost:8080. The admin API listens on port 9091.

Leave the server running. Open a second terminal in the sluice folder:

export PATH="$PWD/bin:$PATH"
cd examples/hello-sluice

2. Send a query

curl -s http://localhost:8080/v1/query \
  -H "X-Api-Key: sl_demo_hello.world" \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT id, email, tenant_id FROM shop.main.customers ORDER BY id"}'
{
  "query_id": "<query-id>",
  "columns": ["id", "email", "tenant_id"],
  "rows": [
    [1, null, "acme"],
    [2, null, "acme"]
  ],
  "row_count": 2,
  "truncated": false
}

The outputs are real, but indented. Values that change between runs appear as placeholders such as <query-id>. Duration fields are left out.

The API key maps to the subject hello with the group analytics and the tenant acme. The policy allow-analytics lets this group read customers and orders.

The row filter kept only the rows of tenant acme, two of five customers.

apiVersion: sluice.bino.bi/v1alpha1
kind: RowFilterPolicy
metadata:
  name: filter-tenant
  priority: 80
spec:
  match:
    any:
      - resources:
          catalogs: ["shop"]
          schemas: ["main"]
          tables: ["customers", "orders"]
  combine: restrictive
  filter:
    predicate:
      column: tenant_id
      op: Equals
      value: "{{ subject.tenantId }}"

The column mask replaced every email with null.

apiVersion: sluice.bino.bi/v1alpha1
kind: ColumnMaskPolicy
metadata:
  name: mask-email
  priority: 50
spec:
  match:
    any:
      - resources:
          catalogs: ["shop"]
          schemas: ["main"]
          tables: ["customers"]
          columns: ["email"]
  mask:
    type: "null"

3. Read a table that no policy allows

curl -s http://localhost:8080/v1/query \
  -H "X-Api-Key: sl_demo_hello.world" \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT * FROM shop.main.admin_users"}'
{
  "code": "ACL_DENIED",
  "message": "no SqlAccessPolicy matched (default-deny)",
  "query_id": "<query-id>"
}

Sluice denies by default. No policy allows admin_users, so the client gets HTTP status 403.

4. Ask which policies apply

sluice policy explain takes a subject and a table. It reads the policy directory and needs no running server.

sluice policy explain --policies-dir policies.d \
  --user hello --groups analytics --claims tenantId=acme \
  --table shop.main.customers
subject : hello
resource: shop.main.customers
decision: allow
Kind              Name             Priority  Effect
----              ----             --------  ------
SqlAccessPolicy   allow-analytics  100       applied
RowFilterPolicy   filter-tenant    80        applied
ColumnMaskPolicy  mask-email       50        applied
row filters:      shop.main.customers
column mask:      shop.main.customers.email  -  null (via mask-email)

5. Verify the audit log

Sluice wrote an audit record for each decision. Each record holds the hash of the record before it.

sluice audit verify data/audit
chain OK (1 file(s), 4 record(s), not keyed, last_hash=<hash>)

chain OK means that the hash chain is intact. The four records are the start of the chain, two for the first query and one for the denied query.

Stop the server with Ctrl+C in the first terminal.

Run it in Docker

The compose file of the example pulls a released image. A release can behave differently from these docs, so build the image from your checkout. You need Docker with Compose, python3 and curl. Run this in the repository root:

docker build -t sluice:dev .
cd examples/hello-sluice
python3 ../seed.py seed.sql data/shop.db
# The container runs as another user and writes the audit log to data/.
chmod -R a+rwX data
SLUICE_IMAGE=sluice:dev docker compose up

The first build compiles DuckDB and takes about 30 minutes. From a second terminal, send the curl commands of sections 2 and 3. Run the sluice command of section 5 inside the container, from examples/hello-sluice:

docker compose exec sluice /usr/local/bin/sluice audit verify data/audit

make docs-check does not run these commands.

See also