[Skip to content](#main-content)

[![Jx Suite](/favicon.svg)
Suite](/)Search⌘KESC

Jump to

[Install Jx Studio](/docs/start/install)[Build your first project](/docs/start/first-project)[Document format](/docs/framework/concepts/documents)[Formula catalog](/docs/framework/reference/formulas)

↵
select↑↓
navigateesc
close

[Studio](/studio)[Framework](/framework)[Templates](/templates)[Compare](/compare)[Docs](/docs/start/first-project)[GitHub](https://github.com/jxsuite/jx)[Download](/download)

✕

[Studio](/studio)[Framework](/framework)[Templates](/templates)[Compare](/compare)[Docs](/docs/start/first-project)[GitHub](https://github.com/jxsuite/jx)

Color scheme&#xA;

[Download](/download)

☰

Docs menu

Start here[Overview](/docs/start/)Get started[Install Jx Studio](/docs/start/install/)[Your first project](/docs/start/first-project/)[A tour of Jx Studio](/docs/start/studio-tour/)Tutorials[Tutorial: interactive component](/docs/start/first-component/)[Tutorial: blog with collections](/docs/start/first-collection/)[Video tutorials](/docs/start/videos/)Coming from another tool[Coming from WordPress](/docs/start/coming-from-wordpress/)[Coming from Webflow or Framer](/docs/start/coming-from-webflow/)[Coming from Wix or Squarespace](/docs/start/coming-from-wix/)[Coming from Lovable or Builder.io](/docs/start/coming-from-ai-builders/)Studio[Overview](/docs/studio/)[The desktop app](/docs/studio/desktop/)The workspace[Overview](/docs/studio/interface/)[Welcome screen](/docs/studio/interface/welcome-screen/)[Editors and views](/docs/studio/interface/modes/)[The canvas](/docs/studio/interface/canvas/)[Documents and panes](/docs/studio/interface/tabs/)[Quick Access](/docs/studio/interface/quick-access/)[Keyboard shortcuts](/docs/studio/interface/shortcuts/)[Preferences](/docs/studio/interface/preferences/)[Languages](/docs/studio/interface/languages/)[Problems and progress](/docs/studio/interface/problems-and-progress/)[Commands](/docs/studio/interface/commands/)Projects[Overview](/docs/studio/projects/)[Create a project](/docs/studio/projects/create/)[Pages, layouts, components](/docs/studio/projects/pages-layouts-components/)[The Content workspace](/docs/studio/projects/browse/)[Starter templates](/docs/studio/projects/starters/)[Media](/docs/studio/projects/media/)[Content types](/docs/studio/projects/content-types/)[Project settings](/docs/studio/projects/settings/)[Dependencies and imports](/docs/studio/projects/dependencies/)Edit mode[Overview](/docs/studio/editing/)[Writing and formatting](/docs/studio/editing/writing/)[Slash commands](/docs/studio/editing/slash-commands/)[Frontmatter and metadata](/docs/studio/editing/frontmatter/)[Grid mode](/docs/studio/editing/grid/)Design mode[Overview](/docs/studio/design/)[Outline](/docs/studio/design/layers/)[Insert](/docs/studio/design/elements/)[Content tab](/docs/studio/design/properties/)[Style inspector](/docs/studio/design/style-inspector/)[Breakpoints](/docs/studio/design/breakpoints/)[States and selectors](/docs/studio/design/states-and-selectors/)[Design tokens](/docs/studio/design/tokens/)[Project Styles](/docs/studio/design/stylebook/)[Working with components](/docs/studio/design/components/)[Repeaters](/docs/studio/design/repeaters/)Logic[Overview](/docs/studio/logic/)[Formulas and expressions](/docs/studio/logic/formulas/)[Formula workspace](/docs/studio/logic/formula-workspace/)[Statements](/docs/studio/logic/statements/)[Logic tab](/docs/studio/logic/events/)[Data](/docs/studio/logic/data/)[Data sources](/docs/studio/logic/data-sources/)[Code editing](/docs/studio/logic/code/)Databases[Overview](/docs/studio/data/)[Connections](/docs/studio/data/connections/)[Data tables](/docs/studio/data/tables/)[Data grid](/docs/studio/data/grid/)[Auth and secrets](/docs/studio/data/auth-and-secrets/)AI assistant[Overview](/docs/studio/ai/)[The AI assistant](/docs/studio/ai/chat/)[Document assistant](/docs/studio/ai/document-assistant/)Publish[Overview](/docs/studio/publish/)[Source control](/docs/studio/publish/source-control/)[GitHub](/docs/studio/publish/github/)[Cloudflare Pages](/docs/studio/publish/cloudflare/)[Other hosts](/docs/studio/publish/other-hosts/)[Redirects](/docs/studio/publish/redirects/)[Real-time collaboration](/docs/studio/publish/collaboration/)Framework[Overview](/docs/framework/)Concepts[Documents](/docs/framework/concepts/documents/)[Elements](/docs/framework/concepts/elements/)[Components](/docs/framework/concepts/components/)[Props and scope](/docs/framework/concepts/props-and-scope/)[State](/docs/framework/concepts/state/)[Reactivity](/docs/framework/concepts/reactivity/)[References](/docs/framework/concepts/references/)[Expressions](/docs/framework/concepts/expressions/)[Statements](/docs/framework/concepts/statements/)[Functions and sidecars](/docs/framework/concepts/functions/)[Styling](/docs/framework/concepts/styling/)[Color schemes](/docs/framework/concepts/color-schemes/)[Popovers and overlays](/docs/framework/concepts/overlays/)[Accessibility](/docs/framework/concepts/accessibility/)[Lists and iteration](/docs/framework/concepts/lists/)[Data prototypes](/docs/framework/concepts/data-prototypes/)[Timing](/docs/framework/concepts/timing/)[Dynamic switching](/docs/framework/concepts/switching/)[Security](/docs/framework/concepts/security/)Site architecture[Overview](/docs/framework/site/)[project.json](/docs/framework/site/project-json/)[Routing](/docs/framework/site/routing/)[Layouts](/docs/framework/site/layouts/)[Content collections](/docs/framework/site/content-collections/)[Jx Markdown](/docs/framework/site/jx-markdown/)[Obsidian vault as content](/docs/framework/site/obsidian-vault/)[Relationships](/docs/framework/site/relationships/)[SEO and metadata](/docs/framework/site/seo/)[Locales and languages](/docs/framework/site/i18n/)[Images](/docs/framework/site/images/)[Redirects](/docs/framework/site/redirects/)[Feeds](/docs/framework/site/feeds/)[Site search](/docs/framework/site/search/)[Build output and adapters](/docs/framework/site/deployment/)How compilation works[Overview](/docs/framework/build/)[The dev server](/docs/framework/build/dev-server/)[CLI commands](/docs/framework/build/cli/)Working with agents[Overview](/docs/framework/agents/)[Authoring rules for agents](/docs/framework/agents/authoring-rules/)[Machine-readable docs](/docs/framework/agents/machine-readable/)Reference[Overview](/docs/framework/reference/)[Formula catalog](/docs/framework/reference/formulas/)[Operator reference](/docs/framework/reference/operators/)Extending[Overview](/docs/extending/)Extensions[Extension anatomy](/docs/extending/extensions/anatomy/)[Schema composition](/docs/extending/extensions/schema-composition/)[Custom classes](/docs/extending/extensions/classes/)[Custom formats](/docs/extending/extensions/formats/)[Capability methods](/docs/extending/extensions/capabilities/)[Project sections](/docs/extending/extensions/project-sections/)[Server mounts](/docs/extending/extensions/server/)[Connectors](/docs/extending/extensions/connectors/)[Search indexes](/docs/extending/extensions/search/)[Security and secrets](/docs/extending/extensions/security/)[Tutorial: a TOML format](/docs/extending/extensions/tutorial-toml-format/)[Tutorial: a guestbook](/docs/extending/extensions/tutorial-guestbook/)[First-party extensions](/docs/extending/extensions/first-party/)Embedding[Overview](/docs/extending/embedding/)[Hosting the Studio assets](/docs/extending/embedding/hosting/)[Writing a platform adapter](/docs/extending/embedding/platform-adapter/)[The backend protocol](/docs/extending/embedding/backend-protocol/)[Dev server internals](/docs/extending/embedding/dev-server/)[Embedding the runtime in your app](/docs/extending/embedding/runtime-host/)[Writing assistant tools](/docs/extending/embedding/assistant-harness/)UI kit[The Jx UI kit](/docs/extending/ui-kit/)Reference[Protocol routes](/docs/extending/reference/studio-routes/)[Implementation status](/docs/extending/reference/implementation-status/)[Spec changelog](/docs/extending/reference/spec-changelog/)[Standards alignment](/docs/extending/reference/standards/)Contributing[Working in the monorepo](/docs/extending/contributing/monorepo/)[Contributing to these docs](/docs/extending/contributing/docs/)[AI assistant evals](/docs/extending/contributing/ai-evals/)

# Auth and secrets

Two related subjects live here: how Studio handles values that must stay secret (database URLs, signing keys, API credentials), and what the auth extension (user accounts and sign-in for your site) adds to Studio.

## Secrets: values stay put, names travel

A hard rule runs through everything database-related: **secret values never enter your project files.** What `project.json` records is only the *name* of an environment variable, such as `MAIN_URL` or `AUTH_SECRET`. The value lives elsewhere:

- **Locally**, values are stored in `.dev.vars` at your project root. It is a plain name-equals-value file, ignored by git, so a commit can never carry a credential. The dev server and the desktop app read it automatically whenever a database, an auth feature or a [server function](/docs/framework/concepts/timing) needs the value.
- **Deployed**, the same names are looked up in your host's environment. You set the values there once. On Cloudflare that means `wrangler secret put` or the dashboard's environment settings.

In Studio you meet this as the **secret field**: settings that hold something sensitive (a Supabase URL in **[Connections](/docs/studio/data/connections)**, the auth signing secret below) render as a password-style box. Paste the value and press Enter or click away; Studio sends it to the backend's secret store, the box empties, and from then on it just reads "Stored as MAIN_URL". The value is write-only, and is never displayed or sent back to the browser again. The derived name is what gets written into `project.json`. To replace a value, paste a new one; to inspect what's stored, open `.dev.vars` itself, because Studio will only ever show you the names.

It is one field, registered once, so every setting that holds a secret behaves identically, including the ones an extension contributes. Where Studio is running against a host with nowhere to keep secrets, the box is disabled rather than taking a value it couldn't store.

Warning

`.dev.vars`
&#x20;is the one place your local secret values exist. It is deliberately not committed, so back it up your own way, and re-enter the values (or copy the file) when moving to another machine.

## The auth extension

The `@jxsuite/auth` extension gives your site user accounts: sign-up and sign-in, sessions, and the table permission rules that depend on knowing who someone is. It's honest to say up front that **most of auth is server-side**. It runs a full authentication service (Better Auth) inside your site, mounted at `/_jx/auth`, and that machinery has no Studio panels of its own. What you see in Studio is three things: a settings section, its account tables in the data grid, and its building blocks for sign-in pages.

### Turn it on

Auth is an extension package, and it keeps its accounts in a database, so it builds on what **[Databases](/docs/studio/data)** already set up:

1. **Have a connection.** Add one in **[Connections](/docs/studio/data/connections)** if the project has none, because the account tables live on one of them.
2. **Install the package.** Type `@jxsuite/auth` into *Settings > Dependencies* and click **Add** (see **[Dependencies and imports](/docs/studio/projects/dependencies)**).
3. **List it as an extension.** Open `project.json` and add `"@jxsuite/auth"` to its `extensions` array, since that list is what switches the section on. Studio has no field for it yet, so this one edit happens in the **[Code editor](/docs/studio/logic/code)**; the array is described in **[project.json](/docs/framework/site/project-json)**.
4. **Pick a server-capable adapter.** Set the build adapter to **Cloudflare Pages**, **Cloudflare Workers**, **Node** or **Bun** under **How it is built** in **[Publish](/docs/studio/publish) › Hosting**; it is `build.adapter` in `project.json`. A site with accounts is no longer purely static, and the build says so if you leave it on Static.

Reopen Settings and an **Authentication** section is waiting. Filling it in writes an `auth` section into `project.json`.

### The Authentication settings section

The section is a single form, and every field is optional. The defaults below are what applies when you leave one blank:

- **connection**: which of your **[connections](/docs/studio/data/connections)** stores the account tables. Defaults to the first one you declared.
- **secretEnv**: the *name* of the environment variable holding the signing secret that protects sessions. Defaults to `BETTER_AUTH_SECRET`. It renders as a secret field: paste any long random string and it's stored as described above. Auth refuses to start without a value for it.
- **methods**: first-party sign-in methods; **emailPassword** (on by default) enables classic email + password accounts.
- **providers**: social sign-in, keyed by provider (`github`, `google`, …), each with a **clientIdEnv** and **clientSecretEnv**. As everywhere, these are env-var *names*; leave them blank and the provider's own defaults apply (`GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET`). Setting one up takes a couple more steps; see **Social sign-in** below.
- **redirects**: **afterSignIn** and **afterSignOut**, the paths a visitor lands on after each. Leave them empty and the page stays where it is.
- **roles**: role names you want to grant (say `admin`, `editor`), usable in table rules as `role:admin`. Declaring any role is also what adds the `role` column to the `user` table.
- **trustedOrigins**: extra origins allowed to call the sign-in routes; most sites leave this empty, since a site's own pages are always allowed.

### Account tables and roles

Auth keeps its users and sessions in ordinary database tables (`user`, `session`, `account`, `verification`) on the connection you chose, and they show up in the **[data grid](/docs/studio/data/grid)** like any other table. That grid *is* the user-management surface today: to make someone an `admin`, open the `user` table and set their `role` cell. Sign-up can never set a role, so roles only come from you.

Since a `user` table carries more columns than that job needs, hide the rest and save the arrangement as a named view ("Roles", say), and the table opens that way every time. **[Saved views](/docs/studio/editing/grid)** are per table.

The tables are created for you two ways: `jx dev` creates them the first time anything touches `/_jx/auth`, and **[Push Schema](/docs/studio/data/tables)** plans them as its own steps after the connector's, so a dry run shows them before anything runs.

Warning

`jx db push`
&#x20;from a terminal covers your&#x20;
**Data Tables**
&#x20;only. The account tables are not part of the CLI push today. A database that only ever received a CLI push has no&#x20;
`user`
&#x20;table, and sign-in fails against it.

### What the rules mean

Declaring auth is what makes table **permissions** beyond `public`/`none` work (**[Data tables](/docs/studio/data/tables)** is where you set them):

- `authenticated`: any signed-in user.
- `owner`: the user a row belongs to. The table's **ownerField** column is stamped with the signed-in user's id on every insert, so visitors can't forge ownership, and reads and writes are scoped to their own rows.
- `role:<name>`: users whose `role` matches.

Without the auth extension these rules simply deny: the door fails closed, never open.

### Sign-in pages

There are no ready-made sign-in components to drop in. You build the pages yourself, from ordinary elements plus two sources the extension adds to the **+ Add…** picker in the **[Data panel](/docs/studio/logic/data)**, alongside every other **[data source](/docs/studio/logic/data-sources)**.

**Session** is who is signed in, kept up to date as that changes. It gives you the signed-in user's **id**, their whole **user** record (email, name, and anything else the account carries), and their **role** when they have one. When no one is signed in it gives you nothing at all. Bind a greeting to the email, and hide the "Sign in" link when a session exists.

### What auth sets up for you

Two defaults are worth knowing because you don't configure them and they're the ones that matter:

- **Session cookies are locked to your site's own hostname.** On a real deployment they're marked `Secure` and named with the `__Host-` prefix, which tells the browser to refuse the cookie if anything tries to set it for a different subdomain or path. A local dev server on plain `http://` skips the prefix, because a browser would reject a `__Host-` cookie there; keeping it would break sign-in on your own machine rather than protect it. Sessions last a week, and are extended when someone uses the site.
- **Auth routes are rate-limited**, in development as well as production: 100 requests per 10 seconds from one address. You won't reach that by hand; a script trying passwords will.

If your site is reached through a different origin than the worker sees (a proxy, or a custom domain in front of it), set `BETTER_AUTH_URL` to the public origin. That's what tells auth whether it's serving over HTTPS, so it's worth setting even if your sign-in providers already work.

One thing to plan around: **the session is always empty at build time.** Your pages are prerendered, and there is no visitor and no browser during a build, so the HTML that ships always shows the signed-out state; the signed-in version appears a moment later, once the page loads and asks the server who is there. That is a consequence of how Jx publishes, not a bug, so keep the signed-out state presentable, and don't put anything at the top of a page that only makes sense to someone signed in.

**Auth actions** are the four handlers a form's submit event points at: **sign in with email**, **sign up with email**, **sign in with a provider**, and **sign out**. Each one reads the fields of the form that submitted it, so the **name** you give each input is the contract:

| Handler        | Reads                                                                                                  |
| -------------- | ------------------------------------------------------------------------------------------------------ |
| `signInEmail`  | `email`, `password`                                                                                    |
| `signUpEmail`  | `email`, `password`, and optionally `name`; without it, the part of the address before the `@` is used |
| `signInSocial` | `provider`, or the default provider set on the state entry                                             |
| `signOut`      | nothing; wire it to a button's click                                                                   |

An input named anything else is invisible to the handler, and the attempt goes out with an empty value.

On success a handler stops the browser's own form submission, refreshes the session, re-runs the page's table queries (so a list scoped to `owner` fills in the moment someone signs in), and sends the visitor to the matching **redirect** if you configured one. On failure (wrong password, an address that already has an account) the page is simply left as it was; surfacing a message is up to you.

Note

The wiring is two state entries and one property on the form. The&#x20;
**[Events](/docs/studio/logic/events)**
&#x20;section of the Inspector's&#x20;
**Logic**
&#x20;tab (
⌘⇧4
) lists plain functions in its handler picker, and an auth handler is one key inside a state entry rather than a function of its own, so point the form at it in the&#x20;
**[Code editor](/docs/studio/logic/code)**
:

```
<span class="jxs-0">{</span>

<span class="jxs-1">  </span>
<span class="jxs-2">"state"</span>
<span class="jxs-3">: {</span>

<span class="jxs-4">    </span>
<span class="jxs-5">"session"</span>
<span class="jxs-6">: { </span>
<span class="jxs-7">"$prototype"</span>
<span class="jxs-8">: </span>
<span class="jxs-9">"Session"</span>
<span class="jxs-10">, </span>
<span class="jxs-11">"timing"</span>
<span class="jxs-12">: </span>
<span class="jxs-13">"client"</span>
<span class="jxs-14"> },</span>

<span class="jxs-15">    </span>
<span class="jxs-16">"auth"</span>
<span class="jxs-17">: { </span>
<span class="jxs-18">"$prototype"</span>
<span class="jxs-19">: </span>
<span class="jxs-20">"AuthActions"</span>
<span class="jxs-21">, </span>
<span class="jxs-22">"timing"</span>
<span class="jxs-23">: </span>
<span class="jxs-24">"client"</span>
<span class="jxs-25"> }</span>

<span class="jxs-26">  },</span>

<span class="jxs-27">  </span>
<span class="jxs-28">"tagName"</span>
<span class="jxs-29">: </span>
<span class="jxs-30">"main"</span>
<span class="jxs-31">,</span>

<span class="jxs-32">  </span>
<span class="jxs-33">"children"</span>
<span class="jxs-34">: [</span>

<span class="jxs-35">    {</span>

<span class="jxs-36">      </span>
<span class="jxs-37">"tagName"</span>
<span class="jxs-38">: </span>
<span class="jxs-39">"form"</span>
<span class="jxs-40">,</span>

<span class="jxs-41">      </span>
<span class="jxs-42">"onsubmit"</span>
<span class="jxs-43">: { </span>
<span class="jxs-44">"$ref"</span>
<span class="jxs-45">: </span>
<span class="jxs-46">"#/state/auth/signInEmail"</span>
<span class="jxs-47"> },</span>

<span class="jxs-48">      </span>
<span class="jxs-49">"children"</span>
<span class="jxs-50">: [</span>

<span class="jxs-51">        { </span>
<span class="jxs-52">"tagName"</span>
<span class="jxs-53">: </span>
<span class="jxs-54">"input"</span>
<span class="jxs-55">, </span>
<span class="jxs-56">"attributes"</span>
<span class="jxs-57">: { </span>
<span class="jxs-58">"type"</span>
<span class="jxs-59">: </span>
<span class="jxs-60">"email"</span>
<span class="jxs-61">, </span>
<span class="jxs-62">"name"</span>
<span class="jxs-63">: </span>
<span class="jxs-64">"email"</span>
<span class="jxs-65">, </span>
<span class="jxs-66">"required"</span>
<span class="jxs-67">: </span>
<span class="jxs-68">""</span>
<span class="jxs-69"> } },</span>

<span class="jxs-70">        {</span>

<span class="jxs-71">          </span>
<span class="jxs-72">"tagName"</span>
<span class="jxs-73">: </span>
<span class="jxs-74">"input"</span>
<span class="jxs-75">,</span>

<span class="jxs-76">          </span>
<span class="jxs-77">"attributes"</span>
<span class="jxs-78">: { </span>
<span class="jxs-79">"type"</span>
<span class="jxs-80">: </span>
<span class="jxs-81">"password"</span>
<span class="jxs-82">, </span>
<span class="jxs-83">"name"</span>
<span class="jxs-84">: </span>
<span class="jxs-85">"password"</span>
<span class="jxs-86">, </span>
<span class="jxs-87">"required"</span>
<span class="jxs-88">: </span>
<span class="jxs-89">""</span>
<span class="jxs-90"> }</span>

<span class="jxs-91">        },</span>

<span class="jxs-92">        { </span>
<span class="jxs-93">"tagName"</span>
<span class="jxs-94">: </span>
<span class="jxs-95">"button"</span>
<span class="jxs-96">, </span>
<span class="jxs-97">"textContent"</span>
<span class="jxs-98">: </span>
<span class="jxs-99">"Sign in"</span>
<span class="jxs-100"> }</span>

<span class="jxs-101">      ]</span>

<span class="jxs-102">    },</span>

<span class="jxs-103">    { </span>
<span class="jxs-104">"tagName"</span>
<span class="jxs-105">: </span>
<span class="jxs-106">"p"</span>
<span class="jxs-107">, </span>
<span class="jxs-108">"textContent"</span>
<span class="jxs-109">: </span>
<span class="jxs-110">"${state.session ? state.session.user.email : 'Signed out'}"</span>
<span class="jxs-111"> }</span>

<span class="jxs-112">  ]</span>

<span class="jxs-113">}</span>
```

`Session`
&#x20;resolves to&#x20;
`{ userId, role?, user }`
&#x20;or&#x20;
`null`
, so&#x20;
`${state.session?.userId}`
&#x20;and&#x20;
`${state.session?.role === 'admin'}`
&#x20;are the expressions to reach for.&#x20;
`AuthActions`
&#x20;resolves to a map of the four handlers, which is why the reference is&#x20;
`#/state/auth/signInEmail`
. Both belong to the browser.&#x20;
`"timing": "client"`
&#x20;is what the Data panel writes for them, and it keeps the build from trying to resolve a session that can't exist yet. Both also accept an optional&#x20;
`baseUrl`
; without one they talk to the site's own&#x20;
`/_jx/auth`
.

### Social sign-in

A provider needs three things, in this order:

1. **Register an OAuth app** with the provider (GitHub, Google, …). The redirect (or callback) URL to give it is your site's origin plus `/_jx/auth/callback/` and the provider's id: `https://example.com/_jx/auth/callback/github`. Add a second app, or a second callback URL, for local development against `http://localhost:3000`.
2. **Store the credentials as secrets**, under the names the settings form shows: `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` unless you changed them. Locally that means `.dev.vars`; deployed, your host's secret store.
3. **Point a form at the social sign-in handler**, with the provider id in a field named `provider` (a hidden input on a per-provider button works well).

Two behaviors to know. A provider whose id or secret has no value is dropped from the configuration entirely. Nothing fails at startup; the sign-in attempt just doesn't work, so check the names match if a button does nothing. And if your site reaches visitors through a different origin than the worker sees (a proxy, or a custom domain in front of it), set `BETTER_AUTH_URL` to the public origin, `https://example.com`; otherwise the origin is taken from each incoming request and the provider can send people back to the wrong place. `BETTER_AUTH_URL` isn't secret, but it's set the same way as the other environment values.

### Current limits

- No emails are sent yet: no address verification and no password reset, so email accounts work immediately after sign-up.
- A table with `insert: "public"` accepts writes from anyone on the internet. Prefer `authenticated` inserts unless you knowingly want an open drop-box.
- Sign-in errors aren't surfaced for you; a failed attempt leaves the form as it was.

## Next

- **[Data tables](/docs/studio/data/tables)** is where the permission rules go.
- **[Data grid](/docs/studio/data/grid)** manages users and roles by editing rows.
- **[project.json](/docs/framework/site/project-json)** records the `auth`, `connections`, and `data` sections as committed config.
- **[Security](/docs/framework/concepts/security)** covers what the server enforces, and what it never trusts.

✕

Start here[Overview](/docs/start/)Get started[Install Jx Studio](/docs/start/install/)[Your first project](/docs/start/first-project/)[A tour of Jx Studio](/docs/start/studio-tour/)Tutorials[Tutorial: interactive component](/docs/start/first-component/)[Tutorial: blog with collections](/docs/start/first-collection/)[Video tutorials](/docs/start/videos/)Coming from another tool[Coming from WordPress](/docs/start/coming-from-wordpress/)[Coming from Webflow or Framer](/docs/start/coming-from-webflow/)[Coming from Wix or Squarespace](/docs/start/coming-from-wix/)[Coming from Lovable or Builder.io](/docs/start/coming-from-ai-builders/)Studio[Overview](/docs/studio/)[The desktop app](/docs/studio/desktop/)The workspace[Overview](/docs/studio/interface/)[Welcome screen](/docs/studio/interface/welcome-screen/)[Editors and views](/docs/studio/interface/modes/)[The canvas](/docs/studio/interface/canvas/)[Documents and panes](/docs/studio/interface/tabs/)[Quick Access](/docs/studio/interface/quick-access/)[Keyboard shortcuts](/docs/studio/interface/shortcuts/)[Preferences](/docs/studio/interface/preferences/)[Languages](/docs/studio/interface/languages/)[Problems and progress](/docs/studio/interface/problems-and-progress/)[Commands](/docs/studio/interface/commands/)Projects[Overview](/docs/studio/projects/)[Create a project](/docs/studio/projects/create/)[Pages, layouts, components](/docs/studio/projects/pages-layouts-components/)[The Content workspace](/docs/studio/projects/browse/)[Starter templates](/docs/studio/projects/starters/)[Media](/docs/studio/projects/media/)[Content types](/docs/studio/projects/content-types/)[Project settings](/docs/studio/projects/settings/)[Dependencies and imports](/docs/studio/projects/dependencies/)Edit mode[Overview](/docs/studio/editing/)[Writing and formatting](/docs/studio/editing/writing/)[Slash commands](/docs/studio/editing/slash-commands/)[Frontmatter and metadata](/docs/studio/editing/frontmatter/)[Grid mode](/docs/studio/editing/grid/)Design mode[Overview](/docs/studio/design/)[Outline](/docs/studio/design/layers/)[Insert](/docs/studio/design/elements/)[Content tab](/docs/studio/design/properties/)[Style inspector](/docs/studio/design/style-inspector/)[Breakpoints](/docs/studio/design/breakpoints/)[States and selectors](/docs/studio/design/states-and-selectors/)[Design tokens](/docs/studio/design/tokens/)[Project Styles](/docs/studio/design/stylebook/)[Working with components](/docs/studio/design/components/)[Repeaters](/docs/studio/design/repeaters/)Logic[Overview](/docs/studio/logic/)[Formulas and expressions](/docs/studio/logic/formulas/)[Formula workspace](/docs/studio/logic/formula-workspace/)[Statements](/docs/studio/logic/statements/)[Logic tab](/docs/studio/logic/events/)[Data](/docs/studio/logic/data/)[Data sources](/docs/studio/logic/data-sources/)[Code editing](/docs/studio/logic/code/)Databases[Overview](/docs/studio/data/)[Connections](/docs/studio/data/connections/)[Data tables](/docs/studio/data/tables/)[Data grid](/docs/studio/data/grid/)[Auth and secrets](/docs/studio/data/auth-and-secrets/)AI assistant[Overview](/docs/studio/ai/)[The AI assistant](/docs/studio/ai/chat/)[Document assistant](/docs/studio/ai/document-assistant/)Publish[Overview](/docs/studio/publish/)[Source control](/docs/studio/publish/source-control/)[GitHub](/docs/studio/publish/github/)[Cloudflare Pages](/docs/studio/publish/cloudflare/)[Other hosts](/docs/studio/publish/other-hosts/)[Redirects](/docs/studio/publish/redirects/)[Real-time collaboration](/docs/studio/publish/collaboration/)Framework[Overview](/docs/framework/)Concepts[Documents](/docs/framework/concepts/documents/)[Elements](/docs/framework/concepts/elements/)[Components](/docs/framework/concepts/components/)[Props and scope](/docs/framework/concepts/props-and-scope/)[State](/docs/framework/concepts/state/)[Reactivity](/docs/framework/concepts/reactivity/)[References](/docs/framework/concepts/references/)[Expressions](/docs/framework/concepts/expressions/)[Statements](/docs/framework/concepts/statements/)[Functions and sidecars](/docs/framework/concepts/functions/)[Styling](/docs/framework/concepts/styling/)[Color schemes](/docs/framework/concepts/color-schemes/)[Popovers and overlays](/docs/framework/concepts/overlays/)[Accessibility](/docs/framework/concepts/accessibility/)[Lists and iteration](/docs/framework/concepts/lists/)[Data prototypes](/docs/framework/concepts/data-prototypes/)[Timing](/docs/framework/concepts/timing/)[Dynamic switching](/docs/framework/concepts/switching/)[Security](/docs/framework/concepts/security/)Site architecture[Overview](/docs/framework/site/)[project.json](/docs/framework/site/project-json/)[Routing](/docs/framework/site/routing/)[Layouts](/docs/framework/site/layouts/)[Content collections](/docs/framework/site/content-collections/)[Jx Markdown](/docs/framework/site/jx-markdown/)[Obsidian vault as content](/docs/framework/site/obsidian-vault/)[Relationships](/docs/framework/site/relationships/)[SEO and metadata](/docs/framework/site/seo/)[Locales and languages](/docs/framework/site/i18n/)[Images](/docs/framework/site/images/)[Redirects](/docs/framework/site/redirects/)[Feeds](/docs/framework/site/feeds/)[Site search](/docs/framework/site/search/)[Build output and adapters](/docs/framework/site/deployment/)How compilation works[Overview](/docs/framework/build/)[The dev server](/docs/framework/build/dev-server/)[CLI commands](/docs/framework/build/cli/)Working with agents[Overview](/docs/framework/agents/)[Authoring rules for agents](/docs/framework/agents/authoring-rules/)[Machine-readable docs](/docs/framework/agents/machine-readable/)Reference[Overview](/docs/framework/reference/)[Formula catalog](/docs/framework/reference/formulas/)[Operator reference](/docs/framework/reference/operators/)Extending[Overview](/docs/extending/)Extensions[Extension anatomy](/docs/extending/extensions/anatomy/)[Schema composition](/docs/extending/extensions/schema-composition/)[Custom classes](/docs/extending/extensions/classes/)[Custom formats](/docs/extending/extensions/formats/)[Capability methods](/docs/extending/extensions/capabilities/)[Project sections](/docs/extending/extensions/project-sections/)[Server mounts](/docs/extending/extensions/server/)[Connectors](/docs/extending/extensions/connectors/)[Search indexes](/docs/extending/extensions/search/)[Security and secrets](/docs/extending/extensions/security/)[Tutorial: a TOML format](/docs/extending/extensions/tutorial-toml-format/)[Tutorial: a guestbook](/docs/extending/extensions/tutorial-guestbook/)[First-party extensions](/docs/extending/extensions/first-party/)Embedding[Overview](/docs/extending/embedding/)[Hosting the Studio assets](/docs/extending/embedding/hosting/)[Writing a platform adapter](/docs/extending/embedding/platform-adapter/)[The backend protocol](/docs/extending/embedding/backend-protocol/)[Dev server internals](/docs/extending/embedding/dev-server/)[Embedding the runtime in your app](/docs/extending/embedding/runtime-host/)[Writing assistant tools](/docs/extending/embedding/assistant-harness/)UI kit[The Jx UI kit](/docs/extending/ui-kit/)Reference[Protocol routes](/docs/extending/reference/studio-routes/)[Implementation status](/docs/extending/reference/implementation-status/)[Spec changelog](/docs/extending/reference/spec-changelog/)[Standards alignment](/docs/extending/reference/standards/)Contributing[Working in the monorepo](/docs/extending/contributing/monorepo/)[Contributing to these docs](/docs/extending/contributing/docs/)[AI assistant evals](/docs/extending/contributing/ai-evals/)

[![Jx Suite](/favicon.svg)
Suite](/)The visual IDE for the web. Build any website, save it as plain files, own it forever. MIT licensed.

Product

[Studio](/studio)[Download](/download)[Templates](/templates)[Features](/features)[Compare](/compare)[Jx Cloud (soon)](/cloud)

Developers

[Docs](/docs/start/first-project)[Framework](/framework)[Get Studio](/docs/start/install)[Spec](/docs/framework)[GitHub](https://github.com/jxsuite/jx)

Project

[MIT License](https://github.com/jxsuite/jx/blob/main/LICENSE)[Releases](https://github.com/jxsuite/jx/releases/latest)[Privacy](/privacy)[Terms](/terms)[Support](/support)

Built with Jx Suite. © 2026 Avunu LLC. Open source under the MIT License.
