Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
523 changes: 488 additions & 35 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ members = [
"examples/basic-worker-rs",
"examples/endpoint-agent",
"examples/byoc-database",
"examples/slatedb-trace-store",
"crates/alien-test",
"tests/e2e/test-apps/comprehensive-rust",
"tests/e2e/test-apps/container-rust",
Expand Down Expand Up @@ -209,6 +210,7 @@ zip = { version = "2", default-features = false, features = ["deflate"] }
pem = "3.0"
oci-tar-builder = "0.4"
sha2 = "0.10"
slatedb = { version = "=0.13.0", default-features = false, features = ["moka"] }
oauth2 = { version = "5.0", default-features = false, features = ["reqwest"] }
cacache = { version = "13.1", default-features = false, features = ["tokio-runtime"] }
strip-ansi-escapes = "0.2.1"
Expand Down
3 changes: 1 addition & 2 deletions crates/alien-bindings/src/providers/storage/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,7 @@ impl ObjectStore for LocalStorage {
) -> ObjectStoreResult<PutResult> {
let dst = prefixed_path(&self.base_dir, location);
if !opts.attributes.is_empty() {
return Err(object_store::Error::Generic {
store: "LocalStorage",
return Err(object_store::Error::NotSupported {
source: Box::new(AlienError::new(ErrorData::OperationNotSupported {
operation: "storage.put object attributes".to_string(),
reason: "the local filesystem backend cannot persist object attributes"
Expand Down
2 changes: 1 addition & 1 deletion crates/alien-bindings/tests/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ async fn local_storage_rejects_object_attributes_without_writing_the_payload() {
.await
.expect_err("local storage must not silently discard object attributes");

assert!(matches!(&error, object_store::Error::Generic { .. }));
assert!(matches!(&error, object_store::Error::NotSupported { .. }));
assert!(error
.to_string()
.contains("the local filesystem backend cannot persist object attributes"));
Expand Down
2 changes: 1 addition & 1 deletion examples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"packageManager": "pnpm@10.11.0",
"scripts": {
"test": "pnpm run test:projects",
"test:projects": "pnpm -C byob-storage-ts check && pnpm -C basic-worker-ts test && pnpm -C remote-worker-ts test && pnpm -C data-connector-ts test && pnpm -C event-pipeline-ts test && pnpm -C command-routing-ts test:ts"
"test:projects": "pnpm -C byob-storage-ts check && pnpm -C basic-worker-ts test && pnpm -C remote-worker-ts test && pnpm -C data-connector-ts test && pnpm -C event-pipeline-ts test && pnpm -C command-routing-ts test:ts && pnpm -C slatedb-trace-store test"
}
}
18 changes: 18 additions & 0 deletions examples/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ packages:
- webhook-api-ts
- nextjs-app
- byoc-database
- slatedb-trace-store
- endpoint-agent
- full-stack-microservices
- full-stack-microservices/services/*
Expand Down
25 changes: 25 additions & 0 deletions examples/slatedb-trace-store/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "slatedb-trace-store"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
alien-error = { workspace = true, features = ["axum"] }
alien-sdk = { workspace = true, default-features = false, features = ["all-platforms"] }
axum = { workspace = true, features = ["tokio", "http1", "json", "query"] }
bytes = { workspace = true }
chrono = { workspace = true, features = ["serde"] }
hex = { workspace = true }
object_store = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sha2 = { workspace = true }
slatedb = { workspace = true }
tokio = { workspace = true, features = ["full"] }
tower-http = { version = "0.6.2", features = ["trace"] }
tracing = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter", "json"] }

[dev-dependencies]
tempfile = { workspace = true }
54 changes: 54 additions & 0 deletions examples/slatedb-trace-store/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# SlateDB trace store

This example deploys durable AI trace history into one customer's cloud. A horizontally scalable HTTP API accepts and queries traces. One background writer commits them to [SlateDB](https://slatedb.io), backed by the customer's object storage.

The deployment is the tenant boundary. There is intentionally no organization ID in the API, queue messages, or storage keys. Deploy another stack for another customer.

## Architecture

| Resource | Lifecycle | Purpose |
| --- | --- | --- |
| `data` Storage | Frozen | SlateDB files, staged traces, and rejected-ingestion metadata |
| `ingestion` Queue | Frozen | Durable handoff from API replicas to the writer |
| `api` Container | Live, 2–4 replicas | `POST` and indexed `GET` requests |
| `writer` Container | Live, 1 replica | The single SlateDB writer and queue consumer |

The API returns `202 Accepted` after the canonical trace object and queue pointer are durable. The trace becomes queryable after the writer commits it and a reader observes the latest SlateDB manifest and WAL. Readers poll once per second.

## Run locally

```bash
alien dev
```

Submit a trace:

```bash
curl -i http://localhost:8080/v1/traces \
-H 'content-type: application/json' \
-d '{
"traceId": "run-01",
"agent": "researcher",
"status": "completed",
"model": "claude-sonnet",
"startedAt": "2026-08-26T18:00:00Z",
"finishedAt": "2026-08-26T18:00:04Z",
"payload": {"events": [{"type": "tool", "name": "search"}]}
}'
```

Read it after it becomes visible:

```bash
curl http://localhost:8080/v1/traces/run-01
curl 'http://localhost:8080/v1/traces?agent=researcher&status=completed&limit=25'
```

Run the behavior tests:

```bash
cargo nextest run -p slatedb-trace-store
pnpm test
```

See the [complete guide](https://alien.dev/docs/examples/trace-history) for the data model, guarantees, failure behavior, and production integration points.
62 changes: 62 additions & 0 deletions examples/slatedb-trace-store/alien.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as alien from "@alienplatform/core"

const data = new alien.Storage("data").lifecycleRules([{ prefix: "staging/v1/", days: 7 }]).build()
const ingestion = new alien.Queue("ingestion").build()

const code = {
type: "source" as const,
src: ".",
toolchain: { type: "rust" as const, binaryName: "slatedb-trace-store" },
}

const api = new alien.Container("api")
.code(code)
.cpu(0.5)
.memory("512Mi")
.port(8080)
.autoScale({
min: 2,
desired: 2,
max: 4,
targetHttpInFlightPerReplica: 100,
})
.publicEndpoint("api", 8080, "http")
.healthCheck({ path: "/health", method: "GET", timeoutSeconds: 2, failureThreshold: 3 })
.environment({ TRACE_STORE_MODE: "api", PORT: "8080", RUST_LOG: "info" })
.permissions("api")
.link(data)
.link(ingestion)
.build()

const writer = new alien.Container("writer")
.code(code)
.cpu(1)
.memory("1Gi")
.port(8081)
.replicas(1)
.healthCheck({ path: "/health", method: "GET", timeoutSeconds: 2, failureThreshold: 3 })
.environment({ TRACE_STORE_MODE: "writer", PORT: "8081", RUST_LOG: "info" })
.permissions("writer")
.link(data)
.link(ingestion)
.build()

export default new alien.Stack("slatedb-trace-store")
.platforms(["aws", "gcp", "azure"])
.add(data, "frozen")
.add(ingestion, "frozen")
.add(api, "live")
.add(writer, "live")
.permissions({
profiles: {
api: {
data: ["storage/data-read", "storage/data-write"],
ingestion: ["queue/data-write"],
},
writer: {
data: ["storage/data-read", "storage/data-write"],
ingestion: ["queue/data-read", "queue/data-write"],
},
},
})
.build()
16 changes: 16 additions & 0 deletions examples/slatedb-trace-store/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "slatedb-trace-store",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"test": "vitest run"
},
"devDependencies": {
"@alienplatform/core": "^1.7.0",
"@alienplatform/testing": "^0.1.0",
"@types/node": "^22.10.5",
"typescript": "^5.7.3",
"vitest": "^3.2.6"
}
}
Loading
Loading