Kjeks — core plugin
The consent engine for WordPress: categories, prior blocking, the banner, and the tracker inventory. Single-site or multisite.
101 Getting started
Install & activate
Download the latest
kjeks.zip,
then in WordPress go to Plugins → Add New → Upload Plugin and upload it.
- Single site: activate it from Plugins. Admin lives under Settings → Cookie Consent.
-
Multisite: the plugin declares
Network: true— Network Activate it. Admin lives under Network Admin → Settings → Cookie Consent.
Kjeks then updates itself from GitHub releases via plugin-update-checker — new versions appear on your Plugins screen like any other update.
Show the banner & preferences
The consent banner renders automatically in the site footer. To let visitors reopen their choices or publish a cookie table, drop in a block or shortcode:
| Purpose | Block | Shortcode |
|---|---|---|
| Reopen the consent banner | kjeks/preferences |
[kjeks_preferences] |
| Cookie declaration table | kjeks/cookie-declaration |
[kjeks_cookie_declaration] |
Set your content
On the Cookie Consent admin page you set the banner heading and body, your privacy-policy URL, and an accent colour. These are stored once and resolved per site.
WordPress privacy page
Kjeks plugs into WordPress' built-in privacy tools:
- Policy Guide: under Settings → Privacy → Policy Guide Kjeks adds suggested text describing the consent categories and how consent works, so you can paste it into your own privacy policy.
-
Live declaration on the privacy page (opt-in): enable
Show the cookie declaration on the privacy policy page on the network
Settings tab and Kjeks appends the live cookie declaration table to the
page set as your WordPress privacy policy. It is skipped automatically when the
kjeks/cookie-declarationblock or[kjeks_cookie_declaration]shortcode is already on that page, so the table is never duplicated.
The consent categories
| Slug | Default | Meaning |
|---|---|---|
necessary | Always on | Essential; cannot be switched off. |
preferences | Denied | Remembers choices like language or layout. |
analytics | Denied | Measurement and statistics. |
marketing | Denied | Advertising and cross-site tracking. |
Everything except necessary is denied until the visitor opts in. You
can add categories with the kjeks_categories filter (see 301), but
every non-necessary category is always optional.
kjeks_honor_gpc filter).
Browser features that hide cookie notices (Brave Shields, uBlock cookie-notice
lists) can also remove it.
201 Consent & integrations
Register an integration
Kjeks fires the kjeks_register_integrations action on
init (priority 20). Register your trackers inside it with
kjeks_register_integration( $id, $args ) — Kjeks then holds those
scripts back until the matching category is granted.
add_action( 'kjeks_register_integrations', function () {
kjeks_register_integration( 'matomo', [
'category' => 'analytics',
'label' => 'Matomo',
'handles' => [ 'matomo-tracker' ],
'src_scripts' => [
'https://cdn.example.com/matomo.js',
[
'src' => 'https://cdn.example.com/extra.js',
'attrs' => [ 'data-site' => '7' ],
],
],
'inline' => [ 'console.log( "loaded after consent" );' ],
] );
} );
Integration arguments
| Key | Type | Description |
|---|---|---|
category | string | One of preferences, analytics, marketing. Unknown or necessary falls back to marketing. |
label | string | Human-readable name shown in the cookie declaration. |
handles | string[] | Registered script handles to gate (blocks an already-enqueued script until consent). |
src_scripts | array | External scripts to inject after consent. A plain URL string, or { src, attrs } to add attributes. |
inline | string[] | Inline JS snippets to run after consent. |
Helper functions
Prefer a one-liner? These wrap the same machinery:
| Function | Use |
|---|---|
kjeks_enqueue_script( $handle, $src, $category, $deps = [] ) | Enqueue a script that loads only after the category is granted. |
kjeks_add_inline_script( $category, $code, $id = '' ) | Run inline JS gated by a category. |
kjeks_embed( $src, $category, $args = [] ) | Return consent-gated embed markup (iframes, pixels). |
kjeks_is_granted( $category ) | Boolean check for server-side branching. |
kjeks_preferences_link( $label = '', $display = true ) | Output/return a link that reopens the banner. |
// Enqueue a script that only loads after a category is granted.
kjeks_enqueue_script( 'my-analytics', 'https://cdn.example.com/a.js', 'analytics' );
// Add inline JS gated by a category.
kjeks_add_inline_script( 'marketing', 'window.startPixel();' );
// Render a consent-gated embed (e.g. a YouTube iframe).
echo kjeks_embed( 'https://www.youtube.com/embed/VIDEO_ID', 'marketing' );
// Branch your own PHP on a granted category.
if ( kjeks_is_granted( 'analytics' ) ) {
// …
}
// Output a "Cookie preferences" link that reopens the banner.
kjeks_preferences_link( 'Cookie preferences' );
Consent on the front end
The visitor's choice is stored in a cookie and localStorage under the
key kjeks_consent, with a six-month lifetime. When consent changes,
Kjeks dispatches events you (and add-ons) can listen for:
window.addEventListener( 'kjeks:granted', () => {
// Fired when the visitor grants (or updates) consent.
} );
window.addEventListener( 'kjeks:withdrawn', () => {
// Fired when the visitor withdraws consent.
} );
sites list applies to the whole network;
otherwise it applies only to the listed blog IDs.
301 Hooks, REST & CLI
Filters
| Filter | Default | Purpose |
|---|---|---|
kjeks_categories | — | Add or modify category definitions. |
kjeks_privacy_url | Policy URL | Override the resolved privacy-policy URL (receives blog ID). |
kjeks_banner_content | Stored content | Override the resolved banner content (receives blog ID). |
kjeks_honor_gpc | true | Whether to honour the browser's Global Privacy Control signal. |
kjeks_is_granted | — | Override the server-side category-grant check. |
kjeks_server_side_gating | false | Enable conditional server-side gating of output. |
kjeks_embed_html | — | Modify consent-gated embed markup. |
WP-CLI
wp kjeks scan-config
Generate a scanner config from the sites and paths WordPress knows about.
wp kjeks scan-config [--paths=<paths>] [--output=<file>] [--include=<ids>]
Outputs { sites: [ { url, blog_id, policy_version, paths } ] } —
feed it straight to the scanner.
wp kjeks import
Import reviewed scanner observations back into the tracker inventory.
wp kjeks import <file> [--blog_id=<id>]
The file accepts either shape:
# A single site
{
"blog_id": 1,
"observations": [ /* … tracker observations … */ ]
}
# Or several sites at once
{
"sites": [
{ "blog_id": 1, "observations": [ /* … */ ] },
{ "blog_id": 2, "observations": [ /* … */ ] }
]
}
wp kjeks scan-key
Manage the shared key the scanner uses to
authenticate against scan-config and import. The key is
sent in the X-Kjeks-Key header, so it works even behind proxies that
strip Authorization (the usual cause of 401 rest_not_logged_in).
wp kjeks scan-key --generate # rotate + print the key (store as KJEKS_SCAN_KEY)
wp kjeks scan-key # print the current key
wp kjeks scan-key --clear # disable key auth
REST API
All routes live under the kjeks/v1 namespace and require manage_network on multisite, or manage_options on a single site. The scan-config and import routes also accept the shared scanner key (X-Kjeks-Key header or scan_key query argument) in place of a capability.
| Method & route | Body / query |
|---|---|
GET /network-config | Returns the current configuration. |
POST /network-config | reviews, remove, add, content, deleteOnUninstall, bannerDefaultVisible, privacyPageDeclaration. |
GET /scan-config | Optional include and paths (comma-separated). |
POST /import | Required blog_id and observations. |
Options & tracker schema
| Option | Scope | Shape |
|---|---|---|
kjeks_network_trackers | Network | Tracker records keyed by tracker ID. |
kjeks_network_content | Network | { heading, body, privacy_url, accent } |
kjeks_network_settings | Network | { delete_on_uninstall, banner_default_visible, privacy_page_declaration } |
kjeks_policy_version | Per site | Integer; bumping it re-prompts visitors. |
Each tracker record carries:
id, name, category, reviewed,
provider, purpose, party,
storage_type, domain, path,
retention, source, documentation_url,
first_observed, last_observed, and sites.