π Lens
August 27, 2026 Β· View on GitHub
π Lens
The developer's viewfinder for the Cratis Stack β become any user, step into any tenant, and fire commands and queries at your running app, straight from the browser.
On a film set, the lens is what the camera sees the scene through β swap it and the whole shot changes: a new angle, a tighter focus, a different depth of field. That's the idea. Lens is the lens you point at your running Cratis Arc application while you build it: watch it as a different user, from inside a different tenant, and focus right in on a single command or query to see exactly what the backend does β no rebuild, no hand-crafted headers, no leaving the page you're already on.
The friction it removes is real: no more pasting identity headers into a REST client, no more restarting the app to switch tenants, no more writing throwaway test harnesses just to see what a query returns.
Arc is the CQRS layer for ASP.NET Core over Chronicle, the MIT-licensed event-sourcing database β and Lens is the development-time companion for any app built on it.
π Why "Lens"?
Three reasons, and they all line up:
- The camera lens sets the point of view. A cinematographer chooses the lens to decide what the audience sees and how it's framed. Lens does the same for your app in development: choose the user, choose the tenant, and every request is framed from that point of view.
- A lens is also what you inspect through. Beyond the camera, a lens magnifies and focuses β it's an instrument for seeing clearly. Lens brings your commands and queries under the glass so you can test them and read back exactly what they return.
- The Cratis storytelling family. Cratis names its products after telling a story: Chronicle records what happened, Arc shapes the plot, Screenplay is the script, Stage performs it, Studio storyboards it, Narrator reads it back⦠Lens is the camera you watch the rehearsal through. It joins the cast.
π₯ What a session looks like
Lens lives in your browser toolbar. Open the popup and you're directing the shot:
- π’ Pick a tenant β the tenancy header is injected into every request; test multi-tenant behavior without touching config or restarting.
- π€ Pick a user β identity, claims, and permissions ride along, so the backend authorizes the request as that person.
- π§ͺ Browse the cast β Lens introspects the app's
/.cratis/commandsand/.cratis/queriesendpoints and lists every command and query, grouped by namespace; fill in parameters, fire it, and read the response inline. - π§ Check the frame β the current tenant, user, and identity context are always visible at a glance.
Nothing about your frontend changes β Lens injects at the network boundary, transparently, only while the extension is active.
π§ How it works β header injection at the boundary
When the extension is active, Lens rewrites the requests your web app makes, adding the headers your backend already expects. Your code is none the wiser:
flowchart LR
You["π§βπ» you<br/>in the browser"] -->|"HTTP request"| Lens{{"π Lens<br/>injects headers"}}
Ctx["π’ tenant Β· π€ user<br/>chosen in the popup"] -.->|"identity + tenancy<br/>headers"| Lens
Lens -->|"tenant-scoped,<br/>authenticated request"| Arc["βοΈ your Cratis Arc<br/>backend"]
Arc -->|"command / query result"| You
- Identity headers β when you select a user, Lens injects the context, claims, and permissions your backend reads for authorization, following the Cratis Arc authentication specification.
- Tenancy headers β when you select a tenant, Lens injects the tenant identifier that routes the request to the right partition, following the Cratis Arc tenancy resolvers specification.
- Automatic propagation β the headers ride along on command execution, query requests, and any HTTP call your app makes, so the backend always receives a properly authenticated, tenant-scoped request.
- A clean cut on every switch β headers alone don't change who you are once the backend has issued a
session. Changing the active user or tenant clears the Arc
.cratis-identitycookie on the configured origins and reloads the matching tabs, so the next request is authenticated as the person you just picked.
β¨ Features at a glance
| Feature | Purpose |
|---|---|
| π’ Tenant Selector | Switch tenants instantly; the tenancy header is auto-injected |
| π€ User Selector | Simulate different users; identity, claims, and permissions follow |
| π Arc Development Sources | Point Lens at a local or remote Cratis Arc backend |
| π§ͺ Command Explorer | Browse and execute backend commands with real parameters |
| π§ͺ Query Explorer | Run queries and inspect the payloads that come back |
| π Query Diagnostics | Watch live observable-query health, transport, multiplexer and cache state |
| π§ Context View | See the current tenant, user, and identity context |
π Quick start
Build the extension and load it into Chrome:
cd Source
yarn install
yarn build # bundles the unpacked extension into Source/dist/
Then, in Chrome:
- Open
chrome://extensions/ - Enable Developer mode
- Click Load unpacked and select the
Source/distfolder
Now open your Cratis Arc app in the browser, open the Lens popup once so it detects the Arc context, then use
Settings to add your Arc Development Sources (local or remote endpoints) and set up the users and
tenants for your scenarios. The full walkthrough β including watch-mode development and release publishing β is
in Documentation/GettingStarted.
For active development with hot reload:
cd Source
yarn dev # Vite rebuilds on change; reload the extension after each build
yarn test # run the specs
yarn ci # typecheck, specs and build β the same gate CI runs
πΊοΈ What's in this repo
Source/
βββ manifest.json # extension manifest (MV3)
βββ main.tsx / LensPopup.tsx # popup UI entry point
βββ background/ # service worker that injects headers
βββ commands/ # command explorer
βββ queries/ # query explorer
βββ observable-query-diagnostics/ # live observable-query health
βββ settings/ # configuration, tenant/user management
βββ context/ # identity and tenancy context
βββ shared/ # shared utilities and types
βββ testing/ # spec helpers (chrome API stub, builders)
βββ arc/ # Cratis Arc integration
Specs sit next to what they specify, in for_<Subject>/when_<context>.ts folders β the same BDD shape as
the rest of Cratis.
β Quality gates
cd Source
yarn typecheck # zero TypeScript errors
yarn test # all specs green
yarn build # the extension builds clean
yarn ci # all three, as CI runs them
π Links
- Cratis Arc documentation
- Authentication & identity
- Tenancy & resolvers
- Cratis Chronicle Β· repository
- Narrator β browse Chronicle event stores from VS Code
- Synopsis β turn your specs into living documentation
- Privacy policy β what Lens does and doesn't collect
π¬ The Cratis ecosystem
This project is part of Cratis β free, MIT-licensed tools for building event-sourced and CQRS applications.
- Chronicle β event-sourcing database and runtime. Orleans-based kernel, pluggable storage (MongoDB default; PostgreSQL, SQL Server, SQLite, in-memory), language-agnostic gRPC contracts. Docs
- Chronicle clients β first-class .NET SDK, plus TypeScript, Kotlin/Java, and Elixir; Python coming soon (pre-alpha). AI agents connect through the Chronicle MCP server.
- Arc β opinionated CQRS framework for ASP.NET Core with commands, queries, validation, authorization, and TypeScript proxy generation. Works without event sourcing. Docs
- Components β React components aligned with Arc patterns. Docs
- CLI + Workbench β inspect and diagnose Chronicle from the terminal or the browser. Docs
- Model-first layer (experimental) β Studio, Screenplay, Stage, Scene, Prologue
- Supporting β Fundamentals, Specifications, Synopsis, Lens (this repository), Narrator, and free AI tooling (preview); Ensemble coming soon (pre-release)
- Samples β runnable event sourcing and CQRS samples for the whole stack
Everything Cratis publishes today is MIT licensed and free to use.
Part of the Cratis platform Β· Licensed under the MIT license