The consent screen for AI agents. Whoever asks for consent also writes the words around it, so this screen shows a person exactly what an agent wants, keeps the agent's words from rewriting it, and hands your app the answer.

Use it when an agent connects to your service, or before your own agent spends, sends or deletes.

It is the human-approval step of KYA-OS, the protocol for agent identity and delegation.

npm install @kya-os/consent

The card is the real component. Untick a permission, then press Allow.

Your app received one event

capabilities-allow


                

That is the whole contract. What it unlocks is up to the trust layer you already have.

  • DIDs and credentials
  • OAuth
  • A policy engine
  • Your agent framework

With a KYA-OS runtime, the approval is recorded as a delegation credential.

Decision log

    Now let's attack it.

    We turned an agent loose on two copies of the same screen, and let it write the words around both. One is plain HTML. The other is @kya-os/consent.

    1. 01 Hostile name

      A name that forges a badge

      The agent is unverified. Its name is markup that adds a green Verified pill and hides the real one. The plain screen builds its headline with innerHTML, so the markup takes effect. The component shows it as text.

    2. 02 Injected CSS

      A rule that hides the expensive part

      One stylesheet rule hides the $500 permission while it stays checked. Page CSS reaches every plain element. It can't reach inside a shadow root.

    3. 03 Page script

      A script that clicks Allow

      querySelectorAll("button") finds the plain Allow button and clicks it. The component's buttons live in shadow roots, so the script never sees them.

    4. 04 Honesty

      Now we cheat

      These shadow roots are open, so code that walks into them on purpose can click the real Allow button. Shadow DOM is encapsulation, not a sandbox. If untrusted code runs in your page, show consent on its own page or origin.

    Plain HTML
    Permission request

    Made by Atlas Agents · Asking from your browser · Connected just now
    Tap to toggle
    @kya-os/consent

    AgentWaiting

    Scroll to run the first attack.

    The wall is a web standard.

    Every component renders into a shadow root, a private DOM built into every modern browser. Page CSS can't style what's inside, and page scripts don't find it unless they go looking.

    Elements panel, for consent-button

    
                  

    Try it on the real thing: right-click any part of this page, choose Inspect, and expand #shadow-root (open).

    No framework required

    Custom elements and shadow DOM ship in the browser. There's no framework runtime to load and no version to keep in step with your app.

    • React is not required
    • Vue is not required
    • Angular is not required
    • Svelte is not required
    • Solid is not required

    We ran the snippets at the bottom of this page with React 19 and Vue 3. Angular, Svelte and Solid support custom elements too; we haven't tested them here. Logos belong to their owners.

    Read the standards

    W3C, World Wide Web Consortium
    Compare with a React or Vue component library
    React or Vue libraryWeb component
    The host needsThat framework, at a shared versionA browser
    It can renderIn apps built with that frameworkOn any page that loads a script
    StylesJoin the page's cascadeStay inside a shadow root
    You talk to it withFramework props and callbacksProperties and DOM events
    What gets reviewedOne build per frameworkOne build for all

    Where it stops

    • It isn't a sandbox. Code in your page can walk open shadow roots, as attack 4 showed.
    • It doesn't verify the agent. The identity pill shows what your app passes in.
    • It doesn't enforce scopes. Your server checks every scope it receives.

    Make it yours.

    Bring your brand color or your Tailwind theme, and the sign-in you already have. Styles reach inside through CSS variables and ::part(), on purpose, and nowhere else. A sign-in step goes in a slot, and the screen around it stays the same.

    Set properties, listen for events. That's the whole contract.