Row-based access for Anfra.
anfra-user puts your Anfra repo behind sign-in. Each person sees only their own rows: their country, their vendor, their accounts.
anfra-user sits between the browser and anfra serve. It signs people in, works out what each one may see, and adds that filter to every query on the server, where nobody can take it off.
A scope says which field limits a user's rows, and which user attribute holds their values.
scopes: - dataset: sales field: customers.country attribute: country default_values: [] # none: nothing
An email and password, plus the values each person may see.
anfra-user users add \ ana@acme.com \ --attr country=Brazil anfra-user users add \ maria@acme.com \ --attr country=__ALL__
Your pages and Anfra Data Apps keep working as they are: anfra-user serves them behind sign-in and adds each user's scope to their queries.
One sales dashboard, one scope on customers.country. Everyone opens the same URL.
| Signed in as | country | Sees |
|---|---|---|
| ana@acme.com | Brazil | Brazil's orders only |
| ben@acme.com | USA, Canada | The USA and Canada |
| maria@acme.com | __ALL__ | Every country |
| new@acme.com | not set | "No country assigned to your account yet" |
The browser never chooses AQL. The gateway refuses to start on anything it can't show is safe, and tests every query as every user.
Pages are parsed on the server. The browser sends a query's name, never its text, so a tampered page runs nothing new.
of_all, exclude, keep, with_relationships and friends can drop a filter. check stops the server if any query or metric uses them.
No attribute, no rows. A dataset without a scope is refused unless you mark it public.
Change a page while serving: it's re-read and re-checked, and a failing query is blocked until it's fixed.
The leak test compares each user's results with the unscoped data and with everything outside their scope. A metric that ignores the filter shows up, whatever AQL produced it.
Made for people who build with AI agents. Install the skill and describe the access you want; the agent does the rest, and checks its work.
Add the plugin, then ask in plain words.
# in Claude Code /plugin marketplace add khanh-tran-quoc/anfra-user /plugin install anfra-user@anfra-user # then just ask "Put this repo behind sign-in. Each sales rep sees only their countries' orders."
Pairs with the Anfra skills for modeling, queries and Data Apps.
Node 23.6+, Docker, and an Anfra repo that anfra serve runs.
git clone https://github.com/khanh-tran-quoc/anfra-user cd anfra-user && npm install docker compose up -d # users database # in your anfra repo, with anfra serve running node ../anfra-user/src/cli.ts check node ../anfra-user/src/cli.ts users add … node ../anfra-user/src/cli.ts verify node ../anfra-user/src/cli.ts serve
Full reference in the README.
permission rules?They aren't enforced in local Anfra yet. anfra-user enforces scopes itself, in a gateway you control. If native enforcement ships, the same idea maps onto it.
Yes, if their queries are fixed text in the file. Point apps: at your apps folder and they run behind sign-in, scoped, unchanged. Plugins (export, render, lineage) aren't available behind the gateway yet.
Only by the gateway. anfra serve has no sign-in, so anyone who can reach it bypasses anfra-user. Keep it on loopback.
No. It's a demo built for fun, MIT licensed.