MantisDocs

Access rules

Decide what the publishable key may read and write, per table and per signed-in user.

The secret key can do anything, so it stays on your server. The publishable key is safe in a browser because it can do nothing until a rule allows it. Rules sit on the table in the schema.

mantis/schema.ts
import { defineSchema, rule, t, table } from "@shipmantis/sdk";

export default defineSchema(
  {
    posts: table({
      title: t.text(),
      published: t.boolean().default(false),
      authorId: t.text(),
    }).rules({
      read: rule.any(rule.where({ published: true }), rule.owner("authorId")),
      insert: rule.owner("authorId"),
      update: rule.owner("authorId"),
      delete: rule.all(rule.owner("authorId"), rule.role("admin")),
    }),
  },
  {
    auth: {
      jwks: "https://example.clerk.accounts.dev/.well-known/jwks.json",
      issuer: "https://example.clerk.accounts.dev",
    },
  },
);

Rules

RuleAllows
rule.public()Anyone with the publishable key.
rule.signedIn()Any signed-in user.
rule.owner("column")Rows whose column holds the signed-in user's id. The column is text or uuid.
rule.role("admin")Users with that role.
rule.where({ column: value })Rows whose columns hold those values.
rule.any(...), rule.all(...)Any one of the rules, or all of them.
  • Deny by default. An operation with no rule is refused for the publishable key.
  • Rows, not tables. owner and where narrow a read, an update or a delete to the rows they allow; the rest are left alone, as if they weren't there.
  • Owners fill themselves in. On an insert, an owner column takes the user's id from the token, whatever the browser sent. An update can't change a column the update rule depends on.
  • The secret key skips the rules. So does the agent, which holds it.

Signed-in users

Mantis doesn't sign users in. It checks the tokens your auth provider already gives them: Clerk, Auth0 or any provider that signs JWTs and publishes its keys as a JWKS.

auth fieldWhat it holds
jwksThe provider's JWKS URL, over https://.
issuerThe iss the tokens must carry.
audienceOptional. The aud the tokens must carry.
roleClaimThe claim that holds the user's role, or a list of roles. Default: role.

A token's sub is the user's id for owner. With a list of roles, rule.role matches any of them. An expired token, or one from another issuer, is refused with 401.

Give the client a function that returns the current token. It is called before every request, so it can hand back a fresh one:

lib/mantis-browser.ts
import { createClient } from "@shipmantis/sdk";
import schema from "../mantis/schema";

export const mantis = createClient({
  url: process.env.NEXT_PUBLIC_MANTIS_URL!,
  key: process.env.NEXT_PUBLIC_MANTIS_PUBLISHABLE_KEY!,
  schema,
  userToken: () => window.Clerk?.session?.getToken(),
});

init writes MANTIS_URL and MANTIS_PUBLISHABLE_KEY to .env.local. A browser bundle only sees what your framework exposes to it, so in Next.js copy them to NEXT_PUBLIC_ names. Never expose the secret key that way.

The token goes in the mantis-user-token header. Without one the caller is signed out, so only public and where rules can allow it.

Changing rules

A rule change is a step in the plan like any other. Making a rule stricter applies at once. A rule that lets more people in is held on main until a person approves it, because it can expose rows that were private. So is a change to auth once the project has tables.

On this page