Reference
WordPress plugin
The dabadata plugin puts live data on WordPress pages. Connect the site to a project in two clicks, map WordPress roles to the project's roles, and add the Live table block: everyone sees rows change as they happen, and logged-in visitors add and edit them under the project's rules. The plugin also mirrors the site's own content changes into dabadata.
Quick start
- Create a dabadata account (free).
- Download the plugin. In WordPress: Plugins > Add New > Upload Plugin, choose the zip, Install Now, Activate.
- Settings > dabadata > Connect to dabadata. Sign in, choose New project from the WordPress template, press Connect. You are back in WordPress, connected.
- Edit a page, add the Live table block, publish. Logged-in editors add and change notes; everyone sees every change live.
Who may do what comes from the project's rules and your role mapping (Roles). The rest of this page explains each step in detail.
Install
Download the plugin zip and upload it in Plugins > Add New > Upload Plugin, then activate it. It needs WordPress 6.5 and PHP 8.1 or newer (with the sodium extension, which PHP ships), and the WordPress server must be able to call https://dabadata.com. The site itself does not have to be public: a local WordPress works.
The plugin is not in the WordPress.org directory yet, so WordPress does not update it by itself: to update, download the new zip and upload it again (WordPress offers to replace the installed version). Your connection and settings stay.
From the repository, plugin/build-zip.sh builds the same zip into plugin/dist/. For local development you can instead symlink plugin/dabadata into wp-content/plugins/, which is what site/setup.sh does for the local test site.
Connect
In Settings > dabadata, check the server address (https://dabadata.com, or your own) and press Connect to dabadata:
- The browser opens the console at
/console/connect?site=<site>&return=<admin url>&state=<random>. Signed out, you sign in or create an account first and land back on it. - The console asks Connect https://your-site to <project>? Pick a workspace, a project (or New project from the WordPress template) and the environment (live by default). The page shows the host the browser goes back to; when it differs from the site's host you confirm it with a checkbox.
- Connect makes a one-time code (
POST /v1/connect/codes) and sends the browser back to WordPress withdabadata_codeand the samestate. - WordPress checks the state, generates a 48-character site secret, and exchanges the code server to server (
POST /v1/connect/exchange). The answer carries the keys; the settings page says Connected to dabadata project prj_... (live).
On the server the exchange stores the site secret as the project secret WP_SITE_<hash>, adds the site as an HS256 identity provider (issuer wp_<hash>, a new definition version by wordpress connect) and mints a secret key for the site, listed on the console's Keys page as WordPress <host>. Connecting the same site again replaces its secret and its key.
Disconnect revokes the site's key on the server and removes the keys and the site secret from WordPress.
The WordPress template
A project made from the wordpress template declares the WordPress roles administrator, editor, author, contributor, subscriber and visitor, and one table for the first block:
"notes": {
"columns": { "title": { "type": "text" }, "done": { "type": "bool", "default": false }, "author": { "type": "userId" } },
"rules": {
"read": "anyone",
"create": { "role": ["administrator", "editor", "author"] },
"update": { "any": [{ "role": ["administrator", "editor"] }, { "owner": "author" }] },
"delete": { "role": ["administrator", "editor"] }
}
}Any other project works too: Connect adds a visitor role when the project has none (the issuer's default role), and the block shows whichever table you name.
Identity bridge
WordPress stays the login. For a logged-in user the plugin signs a short-lived JWT with the site secret, and the page's script hands it to the dabadata client, so the user acts in the project under its rules:
| Claim | Value |
|---|---|
iss | The site's address as the server stored it, e.g. https://example.org |
aud | dabadata |
sub | wp:<user id> - the id the rules see (owner columns hold it) |
name | The display name |
role | The project role mapped from the user's WordPress role |
iat, exp | Issued now, valid 10 minutes |
The page gets { url, key, token } and renews the token a minute before it expires through GET /wp-json/dabadata/v1/token, which needs the WordPress login cookie and the REST nonce (X-WP-Nonce); without them it answers 401. Logged-out visitors get no token: the client is anonymous with the publishable key.
Roles
Settings > dabadata > Roles maps every WordPress role, custom roles included, to one of the project's roles (read from the project's definition). By default a WordPress role maps to the project role of the same name, and anything else to visitor. A user with several WordPress roles gets the mapping of the first. A role the project does not declare counts as visitor on the server too, so a mapping can never make anyone more than the project allows. Changes apply to each visitor's next token, within 10 minutes.
Live table block and shortcode
The block Live table (dabadata/live-table) and the shortcode render the same thing:
[dabadata_table table="notes" columns="title,done" editable="1" orderby="created_at desc" limit="50" title="Team notes"]| Attribute | Meaning |
|---|---|
table | A table of the connected environment. Default notes. |
columns | Comma-separated columns to show. Empty shows every declared column (the block's default is title,done). |
editable | Logged-in visitors get an add form and Edit / Delete on each row; a bool column becomes a checkbox they can tick. Default on for the block, off for the shortcode. |
orderBy | A column, optionally followed by desc (or prefixed with -). Default: oldest first. |
limit | At most this many rows, 1 to 200. Default 50. |
title | A heading above the table. |
On the page the rows arrive over the dabadata socket and every change by anyone shows at once. Rows the visitor owns carry a you badge. A write the rules refuse shows the rule's explanation next to the row, for example Not allowed: update notes: denied, any[0] role "subscriber" not in ["administrator","editor"]; any[1] row.author "wp:7" != user "wp:8". An empty table says so; a logged-out visitor on an editable table sees a log-in link instead of the form.
The scripts load only on pages that use the block or the shortcode: the client from the server (<server>/client/dist/dabadata.iife.js) and the plugin's small table script. With JavaScript off the page still lists the rows an anonymous visitor may read, fetched server side with the publishable key and cached for a minute.
Privacy
- Connecting sends the site's address and the settings page's address to the console (in the browser), and the one-time code, the site's address and the site secret to the server.
- Live tables: visitors' browsers talk to the dabadata server directly - it sees their IP address and browser like any web request. For a logged-in visitor the token carries
wp:<user id>, the display name and the mapped role. Email addresses and passwords are never sent. Rows written through a block are stored in the project with their author'swp:<id>. - The server keeps each call in the project's trace and request log for the plan's
traceDaysandlogDays(see Plans and limits), visible to the project's workspace members in the console. - The mirror sends only what is listed under What is sent.
The plugin's readme.txt carries the same disclosure for the plugin directory. Uninstalling the plugin deletes its options and transients.
Settings
Everything is in the dabadata_settings option:
| Setting | Meaning |
|---|---|
| Server URL | What PHP calls, e.g. https://dabadata.com. It must be reachable from the WordPress server. |
| Browser URL | Optional. The address browsers use for the server (the live blocks and the admin page's viewer) when it differs from the server URL. |
| Publishable key | pk_live_... / pk_test_..., plain - it is sent to browsers anyway. |
| Secret key | Encrypted. Used server side only: the mirror, the viewer's view token, the project's roles. Never printed into a page. |
| Site secret | Encrypted. Signs the identity bridge's tokens. |
| Roles | WordPress role to project role. |
Manual setup (self-hosted servers): paste the server URL, the publishable key (empty for a self-hosted server's default project) and the secret key - or the server's DABADATA_PROJECT_KEY. For the identity bridge, Generate a site secret and declare it as an issuer in the project file, as the settings page shows:
{ "name": "wordpress", "issuer": "https://example.org", "audience": "dabadata",
"secretEnv": "WORDPRESS_JWT_SECRET", "roleClaim": "role", "defaultRole": "visitor" }Export settings downloads the server, browser URL, publishable key and role mapping as JSON - never the secret key or the site secret. Import settings takes that JSON on another site, which then still needs Connect (or the secret key pasted by hand).
The connect API
| Endpoint | Who | What |
|---|---|---|
POST /v1/connect/codes | Console session, developer or higher, X-Project | { site, return } (http(s), no credentials) -> { code, site, expiresAt }. The code is 64 hex characters, single use, 10 minutes, bound to the environment, the site and the return URL. |
POST /v1/connect/exchange | The code in the body (30 per IP per hour) | { code, site, siteSecret } -> { project, env, publishableKey, secretKey, site, issuer, roles }. A used, expired or other site's code answers 400 code_invalid. |
The mirror
Beside the blocks, the plugin streams the site's own content changes to dabadata with the secret key, and the dabadata admin menu shows them live.
What it hooks
| Component | WordPress hooks |
|---|---|
post:<type> | wp_after_insert_post, deleted_post - every post type except internal ones such as revisions, menu items and changesets. |
| post meta | added_post_meta, updated_post_meta, deleted_post_meta (keys not starting with _), sent as a change on the post. |
user | user_register, profile_update, set_user_role, deleted_user |
comment | wp_insert_comment, edit_comment, transition_comment_status, deleted_comment |
term:<taxonomy> | created_term, edited_term, delete_term |
wc:order | woocommerce_new_order, woocommerce_update_order, woocommerce_order_status_changed, woocommerce_delete_order, woocommerce_trash_order - only when WooCommerce is active. |
Each event says what happened (add, update or remove), to which record, who did it, the record's current fields and, for updates, what changed from what to what. All events of one page load are sent together in one request, when WordPress shuts down, so a save never waits on dabadata.
What is sent, and what never is
- Posts: status, slug, author, dates, a 200-character excerpt and the content length - not the body.
- Users: login, display name, roles, registered date.
- Comments: post, author name, status, type.
- Every string is cut to 500 characters.
If the server cannot be reached, the site carries on; the failure is written to the PHP error log with a [dabadata] prefix.
The dabadata admin page
The dabadata menu, for administrators, shows whether the server is reachable, the Send snapshot button, and the dabadata viewer embedded in the page. The plugin mints a view token with the secret key for the viewer and caches it for 11 hours, under the token's 12-hour lifetime. The shortcode [dabadata_inspect] puts the same viewer on any page; it renders for administrators only.
In the viewer, the Mirror page is the WordPress flow: every event as it arrives and the current state of each record, grouped by component.
Send snapshot
The plugin only sees changes made after it was activated. Send snapshot backfills everything that already exists - every post, user, term and approved comment - as add events marked snapshot: true, read in batches of 100 and sent in batches of 200. Press it once after installing, and again whenever you want the mirror to match the site exactly.
The mirror API
The plugin speaks the original (v0) part of the API, which sits beside the v1 database:
| Endpoint | Who | What |
|---|---|---|
POST /v1/events | Secret key | { "events": Event[] }, 1 to 500 per call. Each is stored and applied to the record it names, then broadcast to view-token sockets. Answers { "accepted": n }. |
GET /v1/events | View token | Stored events, newest first. limit, before, component. |
GET /v1/components | View token | Each component with its record count, event count and last activity. |
GET /v1/records | View token | Current records, newest updated first. component, limit. |
{
"source": "wordpress",
"site": "http://localhost:8080",
"component": "post:page",
"action": "update",
"entityId": "42",
"title": "About us",
"actor": { "id": "1", "name": "admin", "role": "administrator" },
"data": { "status": "publish", "slug": "about-us", "author": "1" },
"changes": { "title": { "from": "About", "to": "About us" } },
"snapshot": false,
"ts": 1760000000000
}