The project is in a healthy, maintained state
kiosk-rls-minitest wires the framework-agnostic Kiosk journey-test DSL (kiosk-test-support) into Minitest. `include Kiosk::TestHelpers` inside any `Minitest::Test` subclass mixes in the journey helpers (`as_agent_of`, `as_user`, `as_agent`, `as_anonymous`, `query`, `run_query`, `run_action`, `pay_action`, `kiosk_seed`). Ships the assertions `assert_rls_denied` and `assert_quota_exceeded` for asserting the structured error classes — both also exposed as the Minitest `must_raise_*` / `wont_raise_*` spec-DSL forms.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

~> 0.5.0
>= 5, < 7
 Project Readme

Kiosk — OSS reference implementation

Apache-2.0 monorepo for Kiosk — the standards-based open protocol that lets your customers' AI assistants discover, transact with, and pay your service, and its Ruby reference implementation (REST endpoint, multi-assistant identity per user, app-layer-authorized data plane with opt-in Postgres RLS defense-in-depth, AP2 mandate trail).

Install

No Kiosk gem is on RubyGems. MEASURED 2026-09-06: every gem name in the tables below answers HTTP 404 from https://rubygems.org/api/v1/gems/<name>.json against a control of rails → HTTP 200; this repository carries no release tag, and no gemspec sets allowed_push_host. So a bare gem "kiosk-all", and bundle add of any of these names, do not resolve today — git is the only install that works. This section is where publication status is stated canonically — the one place either published repository decides it — and every install snippet elsewhere points here instead of restating it.

One sentence is copied rather than pointed at, deliberately: the onboarding guide on kiosk.tech carries the bolded claim above and a link back to this section, because a reader deciding whether to start an integration should not have to leave the page to find out the gems are not published. Rewording it here means rewording that page too.

The canonical install is one line — the meta-gem, from git:

# Gemfile
gem "kiosk-all", github: "kiosk-hq/kiosk"
bundle install
bin/rails generate kiosk:install   # the initializer + the kiosk migrations
bin/rails db:migrate

kiosk-all is kiosk-core + kiosk-server — the data plane, and nothing else. The RLS backstop, the PoW backends and the IdP/PSP adapters are deliberately separate gems you add per stack, each from the same source; kiosk-all/README.md says which and why. When the gems are published this section is where the github: qualifier comes off, and the snippets that point here inherit it.

What an adopting app needs before that line

Requirement Why, and what it rules out
Rails ~> 8.1 kiosk-server declares railties, actionpack, actioncable, activerecord and activesupport at ~> 8.1, and kiosk-pay-stripe declares activerecord at ~> 8.1. No other gemspec names a Rails gem. That excludes Rails 7.x and 8.0.x — bundler will refuse to resolve against an app on either, so this is a precondition and not a footnote. Older Rails lines are untested here, so they are not claimed; widening the floor means adding a CI leg first.
Ruby >= 4.0 Every gemspec declares it, and CI runs on 4.0.
PostgreSQL The kiosk schema, the identity tables and the optional RLS backstop are Postgres. No other database is supported.
An existing account model c.user_model names it, and c.user_id_type must match its primary key. Kiosk adds an assistant identity beside your users; it does not replace them.

No toolchain pin ships in this repository, and that is a decision rather than an omission: .ruby-version, mise.toml and .mise.toml are gitignored so each developer manages their own (kiosk-demo-getgrocery/mise.toml.example is the tracked template). The range above is the contract, both ends of it are on the merge gate, and that is worth more to a contributor than one pinned patch release a version manager may or may not honour.

Inside this repository it is path:, and that form does not travel

Every Gemfile here consumes its siblings by path: — gem "kiosk-server", path: "../kiosk-server" — which serves the working tree rather than a built package. That is deliberate: it is how a change across gems is tested end to end. It is also a blind spot, because nothing resolving by path: can tell a file that EXISTS from a file that is PACKAGED. Do not copy the path: form into an app outside this repository.

The step-by-step operator walkthrough — install through a served wire, with a worked verb — is kiosk.tech/onboarding.html.

Layout

The gem tables and the demo table are scored on DIFFERENT axes, so their values are not comparable and the columns are named apart below. The gems carry API stability: alpha is pre-v1.0 — the surface may still change between releases — which is the same thing each gem's own README says about itself. The demos carry deployment: active means the demo is gated on every CI run and live at its own subdomain. A demo being active says nothing about how settled the alpha engine underneath it is.

Core framework

Gem Purpose API stability
kiosk-core Value types, abstract bases, GUC constants, configuration. No Rails dep. alpha
kiosk-rls Opt-in RLS DSL + migration helpers; declares policies in a migration and compiles them to PostgreSQL DDL alpha
kiosk-server Rails engine, routes, kiosk-pop auth surface, executor alpha
kiosk-all Meta-gem; bundle add kiosk-all installs core + server — once the gems are published; today it is one github: line, see Install alpha
kiosk-test-support Conformance checks, the assistant-side wire client and a KYC provider stand-in for tests alpha

Plugins & adapters

Gem Purpose API stability
kiosk-rls-rspec RSpec journey-test helpers for RLS policies alpha
kiosk-rls-minitest Minitest journey-test helpers for RLS policies alpha
kiosk-user-idp-devise User-IdP adapter (Devise) alpha
kiosk-pay-stripe PSP adapter — Stripe, AP2 mandate trail alpha
kiosk-kyc-prove KYC provider adapter — the Prove anonymizing broker alpha

Proof-of-work

Gem Algorithm Solver memory Default?
kiosk-pow-equihash Equihash (n=168, k=7) ~1.3 GiB ✅
kiosk-pow-argon2id Argon2id (D=0..256) 64 MiB legacy
kiosk-pow-cuckoo Cuckatoo29 not measured no

Each backend is built on kiosk-pow, which holds the shared BLAKE2b-256. kiosk-pow/README.md tells how to add a backend. See kiosk-pow-equihash/README.md for the full comparison and rationale.

Reputation & security

Gem Purpose API stability
kiosk-reputation Customer reputation factors alpha
kiosk-redteam Red-team scenarios, adversarial test harness alpha

Demo providers

Demo Vertical Deployment
kiosk-demo-getgrocery Grocery delivery active
kiosk-demo-atablefor Restaurant table-booking — non-commerce (no payments) active
kiosk-demo-hoteling Hotel booking active
kiosk-demo-skooti Scooter rental active
kiosk-demo-stylish Salon/appointment booking (Combette on Park) — non-commerce (no payments) active
kiosk-demo-philslist Classifieds board — non-commerce (no payments) active
kiosk-demo-tudu Collaborative todo — non-commerce (no payments) active
kiosk-demo-prove anonymizing KYC broker (deploys at kyc.demo.kiosk.tech) — an ISSUER, not a Kiosk operator (no verbs) active

e2e/ is not in that table, because it is not a ninth provider. It is the end-to-end test harness — fixtures, a stripe-mock-backed pay flow and the AI-assistant drivers — so it serves no vertical and deploys nowhere. It runs as a CI gate (./e2e/run.sh) and nothing else.

Four more top-level directories are neither a gem nor a demo. bin/ holds the checks that gate this repository — one check-* script per property, each exiting non-zero when that property breaks; CI runs them as jobs of their own, not through rake. deploy/ is the runbook for hosting the live demo fleet — Caddy, Postgres, systemd and the scripts that provision and smoke-test the box (deploy/README.md). spec/ holds three GENERATED indexes the publication and conformance checks read and regenerate — not a test suite; each gem's own tests live under its own spec/. .github/ holds the one CI pipeline, workflows/ci.yml — the gems matrix, the demos matrix and e2e/run.sh, on every push and pull request.

Each demo's tests drive it over HTTP the way an assistant does; CI runs them with bin/rails test (bundle exec rspec in hoteling).

Four of the demos — atablefor, getgrocery, hoteling, skooti — also carry a before-after.md: a contrast between what an AI assistant can do at that provider today and what the same errand looks like once Kiosk is installed, followed by the operator-side adoption recipe. Four rather than all eight is deliberate. Every fenced block in one declares what produced it. The rest say it shorter: philslist carries an inline Before / after section in its README; stylish and tudu carry neither.

The demos are standalone Rails apps, so a helper two of them need is COPIED, not shared. bin/check-demo-copies — its own CI job too — declares every hand-written Ruby file that exists in more than one demo, plus .gitignore, as kept identical, kept identical apart from its header prose, or a deliberate per-demo variant, each with the reason; it fails the build when copies that must agree stop agreeing, and when a new duplicate turns up undeclared. Header prose may vary, but a magic comment may not: # frozen_string_literal: true changes how the file runs, so it is compared. The Rails skeleton each demo edits for its own port and host is out of scope on purpose, and that exclusion is recorded file by file, with its reason, in the same script.

Everything in this repo consumes the gems by path:, which serves the working tree — so nothing here can tell a file that EXISTS from a file that is PACKAGED, and kiosk-server shipped without its view templates for exactly that reason. Adding a non-lib/ file a gem needs at runtime means adding it to that gem's spec.files.

Contributing

CONTRIBUTING.md is the whole of it: how to run one gem, one demo, the end-to-end harness, bin/lint (RuboCop, bundler-audit, Brakeman) and the bin/check-* guards; what the merge gate is; and what this repository expects of a changelog entry and a comment. A security flaw goes through a GitHub issue — read SECURITY.md first, for which repository takes it and for the fact that filing one is public disclosure.

Before 1.0 every gem carries the protocol's full version — the version parity the spec promises (protocol §14.1): the protocol, every gem here and the published skill read the same MAJOR.MINOR.PATCH, and a new cut of any one of them moves all of them. From 1.0 they share MAJOR.MINOR. The number is Kiosk::Protocol::API_VERSION, and the ~> MAJOR.MINOR.0 inter-gem constraints, the lockfiles and the pinned skill_url follow it. Releases are cut as path-scoped git tags — the gem's directory name, then /v and the version — off each subdir's authoritative *.gemspec.

The two development keypairs this repo tracks on purpose

kiosk-demo-skooti/config/dev_unlock_key.pem (Ed25519) and kiosk-demo-prove/config/dev_prove_key.pem (RSA) are private keys, they are committed, and that is deliberate. Read the header of either file: it says what the key is for, that it is world-readable, and that it must never sign anything real. Both applications enforce that rather than asking — production refuses to boot without an explicit key in the environment, and refuses again if the key it is given is the shipped one, compared on the public half in DER so a re-serialised copy cannot slip past.

The Ed25519 one cannot be generated per machine: it is a KNOWN-ANSWER VECTOR. Its public half and one signature are reproduced in the lock firmware, in the C host test that firmware ships with, and in the demo's own known-answer test, and the crosscheck target signs a live message with the private half for the C verifier to check. A per-machine key turns all of that red on a fresh clone.

Do not read this as licence to commit a key. A scanner pointed at this repo will flag both files, and that is the correct behaviour — which is why .github/secret_scanning.yml names exactly these two paths and nothing else. bin/check-publication-paths fails on any OTHER tracked private key, and on either of these two if it ever loses its do-not-use banner; it re-proves that detector against a freshly minted key on every run, so it cannot rot into a gate that quietly matches nothing. And note the part that no later commit repairs: this project does not rewrite published history, so a key that reaches a pushed commit stays reachable in that history whatever a subsequent commit deletes. Generate at setup or read from the environment; if a fixed vector is genuinely unavoidable, declare it in that script with the reason.

License

Apache-2.0 for every gem in this repo. See each gem's LICENSE.txt.

Every Kiosk gem lives in this repo and under that licence: ls */*.gemspec is the whole list, and each one declares spec.license = "Apache-2.0".

Links