Cloud¶
Concept¶
The SDK is complete without this. pip install rewyn records, replays,
evaluates and gates with no account, no key and no network. That is the
boundary spec §47 draws, and it is deliberate: the open-source half has to
be genuinely useful on its own.
The cloud adds what a team needs and a laptop cannot provide: shared runs,
search across everyone's work, centralised datasets, and evaluation history
that outlives one developer's .rewyn/ directory.
Where it lives¶
The service itself is not part of this repository. This package is the client
half: rewyn login, rewyn sync and the remote store below are open source
and fully specified, so the protocol a deployment speaks is inspectable even
though the server is commercial. See https://rewyn.dev for the hosted
service.
Nothing on this page is required. The SDK records, replays, evaluates and gates with no account and no network, and it always will — that boundary is the point, not a limitation to be removed later.
Connecting the SDK¶
rewyn login --endpoint https://api.rewyn.dev --key rw_...
rewyn sync # upload local runs
rewyn sync --datasets --evaluations
rewyn sync --status # what is configured and what is queued
rewyn sync --pull run_01J... # fetch a run to replay locally
rewyn logout
Credentials are written to .rewyn/credentials.json with owner-only
permissions, and the key is registered with the redactor so it cannot appear
in a recorded event. REWYN_API_KEY and REWYN_ENDPOINT override the
file.
From code¶
from rewyn.storage.remote import RemoteStore
store = RemoteStore()
await store.apush_runs() # every local run
await store.apush_dataset(dataset)
await store.apush_report(report)
listing = await store.alist_runs(q="refund", status="failed", limit=20)
await store.apull_run(listing["runs"][0]["run_id"]) # then replay it locally
Sync never raises into your application. Failures are collected into a
SyncResult, and the run ids are queued locally and retried on the next
sync, so a laptop that was offline catches up rather than losing runs. Pass
strict=True when you do want the exception.
Teams, roles and governance¶
Four roles, ordered, so each can do everything the one below it can.
| Role | Can |
|---|---|
viewer |
read runs, datasets and evaluations |
member |
also upload runs and write datasets |
admin |
also manage keys, alerts and retention, and delete runs |
owner |
also manage the team and its members |
Keys carry a role, so a CI key that uploads runs cannot delete them, and a key can never mint one more powerful than itself. The blast radius of a leaked key should be the job it was minted for.
Teams own projects and members. SSO maps a verified assertion to a team by email domain and provisions the member on first sight; whatever terminates SSO in front of the service is what verifies the identity.
curl -X POST "$API/v1/teams" -d '{"name":"Acme","sso_domain":"acme.com"}' ...
curl -X POST "$API/v1/team/members" -d '{"email":"dana@acme.com","role":"admin"}' ...
curl -X POST "$API/v1/sso/login" -d '{"email":"dana@acme.com","provider":"okta"}'
Access changes and deletions are audited. The log is scoped to your project or your team, and readable by an admin:
Retention is a per-team window, applied explicitly. Nothing deletes data on a timer inside the service; call it from a scheduled job you can see and stop.
curl -X PUT "$API/v1/team/retention" -d '{"days": 90}' ...
curl -X POST "$API/v1/retention/apply" ...
Shared incidents, comments and views¶
The cloud console serves the same screens as rewyn ui, over the same
contract, with one difference that only a team has: the things people write
are shared.
An incident is detected from the project's runs on every request, so it needs
no storage. What is stored is the human half -- who took it, what status it
is in, and the trail of both -- alongside comment threads and saved views.
All three are scoped to the project, readable by any viewer and writable by
member and above.
curl "$API/console/v1/incidents" -H "Authorization: Bearer $KEY"
curl -X POST "$API/console/v1/incidents/errors-2d711642b7" \
-d '{"status":"investigating","assignee":"dana@acme.com"}' ...
curl -X POST "$API/console/v1/comments" \
-d '{"subject":"run:run_01J...","body":"this is the slow one"}' ...
curl "$API/console/v1/views?screen=runs" -H "Authorization: Bearer $KEY"
A comment's subject must be something the console can open -- a run, an incident, a dataset, an agent, a release or a saved view -- so a thread cannot be orphaned by a typo. The author is the key's label, which is what identity means on a surface authenticated by keys.
Alerts and dashboards¶
Thresholds on error_rate, cost_per_run, total_cost, avg_latency_ms
or run_count over a window. Evaluation is pull-based: a push notifier
would put an outbound network call on the ingest path, which is where the
performance requirements say nothing slow belongs.
curl -X POST "$API/v1/alerts" \
-d '{"name":"errors","metric":"error_rate","threshold":0.05,"window_minutes":60}' ...
curl -X POST "$API/v1/alerts:evaluate" ... # returns the ones that fired
curl "$API/v1/stats?window_hours=24" ... # the figures a dashboard renders
/v1/stats returns data rather than a rendering, so a web UI, the CLI and
an alerting job all read the same numbers.
Architecture¶
Run manifests are rows, so they can be searched and aggregated. Event logs are blobs behind a two-method object-store protocol, so a large run does not bloat the database and S3 replaces the filesystem backend without touching the service:
from rewyn.integrations.s3 import S3ObjectStore
create_app(store=S3ObjectStore("rewyn-runs", prefix="prod/"))
| Variable | Default | Meaning |
|---|---|---|
DATABASE_URL |
SQLite under the home | PostgreSQL in production |
REWYN_CLOUD_HOME |
./.rewyn-cloud |
Database and object storage root |
REWYN_CLOUD_STORAGE |
<home>/objects |
Object storage directory |
REWYN_CLOUD_OPEN_REGISTRATION |
off | Let anyone create a project |
API keys are stored only as SHA-256 hashes with a short non-secret prefix. Every route below the project level is scoped to the calling key's project.
API reference¶
rewyn/storage/remote.py for RemoteStore, Credentials, SyncResult
and SyncQueue — the whole client half of the protocol.
rewyn/ui/schemas.py for the wire types the console reads, which the
service returns unchanged.
Failure modes¶
NotAuthenticatedError. No key configured, or it was revoked. Run
rewyn login. rewyn sync --status shows what is configured without
printing the key.
Syncing needs httpx. pip install "rewyn[remote]".
Uploads silently do nothing. Check rewyn sync --status for a queue.
Failed uploads are queued, not lost, and retried on the next sync.
A pulled run will not replay. Pull fetches the manifest and the events into local storage; replay reads from there. If the run was ingested without its events, there is nothing to substitute.
Timestamps look wrong. SQLite has no timezone type. The API reattaches UTC on the way out, so treat anything naive from a direct database read as UTC.
A request returns 403. The key's role is below what the operation needs. The message says which role would be enough.
One project sees another's runs. It cannot; every query is scoped by the key's project. If you are seeing cross-tenant data, you are using one key for two tenants. Use one project per tenant.