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:
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:
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.
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:
make docs-check does not run these commands.