Legal
Privacy Policy
Agent Vault lets an AI agent check out and sign in on your behalf without ever holding your real card number, password, or one-time-code seed. Doing that means we hold those things instead. This page says exactly what we hold, who else sees it, how long it stays, and where our current implementation falls short of what we intend.
1. Who we are
Agent Vault is a product of Self.xyz, operated by SocialConnect Labs, Inc. dba Self Labs, a Delaware corporation (“we”, “us”). For data you store in Agent Vault we are the controller: we decide what is collected and why.
Privacy enquiries and data-protection correspondence both reach us at legal@self.xyz. We have not appointed a separate data protection officer, and we have not appointed an EU or UK representative under Article 27 of the GDPR. If you are in the EEA or the UK and need to reach us about your data, use the address above.
This policy covers the Agent Vault web app and dashboard, the checkout proxy that fills forms on your behalf, and the backend that stores your vault. It does not cover the merchant sites and login sites your agent visits, those are independent controllers with their own policies.
2. What we collect
Almost everything below is data you deliberately put in, because the product cannot fill a checkout form with data it does not have. We do not buy data, and we run no advertising and no third-party tracking. We do run product analytics on our own website, pageviews and a few product events, with no profile of you attached to them (see §13). We also keep our own operational telemetry about agent sessions. See below.
Account and identity
- Email address and account identifier, from our authentication provider when you sign in.
- First name, last name, and phone number, if you enter them, so an agent can complete a checkout’s contact fields.
- Your plan (Free, Plus, Pro), billing identifiers issued by Stripe, and your notification preferences.
Addresses
Full postal addresses you save, label, street lines, city, state/region, postal code, and country, used as shipping and billing addresses at checkout.
Payment cards
Your card number is sent from your browser directly to our card vault provider (see §5). For a card you add now, our database holds a vault token reference and non-secret card metadata: last four digits, brand, expiry month and year, the cardholder name you enter, and a link to the billing address you chose. Legacy rows from the pre-vault design can still carry encrypted card data until the purge job clears them. §5 has the detail.
We do not store the card security code (CVV/CVC) for cards added now. When a merchant demands one, we ask you for it at the moment of the purchase; it is never written to our database, and lives only in the proxy’s memory for that session (§5). A column holding security codes under the previous design is still being purged (§5).
Site logins
For each site you connect, or that an agent connects for you (§4), we store a label, the site URL, an optional note, the username, the password, and, if one is added, the seed for time-based one-time codes (TOTP). The password and the TOTP seed are encrypted at rest; the username and site are stored in readable form so the dashboard can list them.
Where an agent made the login rather than you, we also record which of your API keys it used and whether the stored password is still the one we generated, so the dashboard can tell you plainly that an account exists which you never typed a password for. If an agent set up that login’s two-factor, we record when it did and on what host, plus the id of the proxy session that did it. That last one is not descriptive: it is what lets that same session, and only that session, take the seed back if the site rejects the first code. It is held for that purpose and is not part of a data export.
We also store any additional sign-in hosts you authorize for that login. Real sign-ins redirect, and one site can hand off to another to collect your password, but the site you saved does not cover a hand-off to a different domain. These extra hosts are only ever ones you add: nothing is inferred from where a page tried to send the browser, because following a redirect wherever it leads is precisely what this lock exists to prevent.
The two are matched differently, and the difference is worth knowing. The site you saved also covers its subdomains: save example.com and a sign-in on login.example.com is allowed, though notexample.com never is. Each extra host you add is matched exactly, and authorizes nothing beneath it.
If you fill in Additional sign-in sites, we store those too, up to ten per login. They are ordinary stored data: the dashboard lists them, a data export includes them, and your agent’s token catalog carries them beside the primary host (§4), so the whole set of sites you have authorised one login for reaches the agent’s context and its model provider. That set says something about you, which is why it is named here rather than folded into “the site URL”.
Agent configuration
Agent cards and API keys you create, including their names, spending limits, merchant allowlists, usage counters, and any webhook URL you configure. An API key you create today is stored as an irreversible hash, never the key itself, alongside the first few characters of it, so the dashboard can show you which key a row refers to.
An older agent card, created under a design that has since been replaced, may still carry a proxy credential of its own on the record. Those columns are no longer written, a data export strips them, and the dashboard no longer sends them either: the listing is built from an explicit allowlist of fields that does not include them, so that value no longer reaches your browser. A scheduled migration is clearing the columns from the records that still hold them. Until it has reached every row we will not claim we hold no key material anywhere, the value may still exist at rest on an old record, which is why this paragraph stays until the purge completes. Issue #213.
Checkout recipes
Before a checkout, your agent asks us whether we have a recipe for the site it is on. That lookup sends the merchant’s host and page path to our backend, and it happens on ordinary checkouts whether or not you ever contribute anything. It carries no API key, so the request does not itself name your account, but that is not the same as being unlinkable: it arrives from the same machine, at about the same time, as your agent’s authenticated calls, so the operational logs in §2 could be correlated to connect the two. It tells us which site an agent is about to buy from, and it can appear in our request logs and those of the networks in between.
If your agent calls POST /v1/recipes, a step the bundled agent skill performs, we store a registry contribution linked to your account: the full recipe, the optional free-text note you send the maintainer with it, its review state, and timestamps. We keep the first 2,000 characters of that note and drop the rest, so put the part that matters first.
That recipe and note are sent to Anthropic to be screened before publication, and a recipe that passes is written into our public registry, where it is no longer associated with you. That is a statement about the LINK, not about the contents: we drop the account it came from, and we do not edit the recipe. It is published as you sent it, extra fields and all, so an identifier you put inside one is published with it. The entry itself may later be replaced or merged into another recipe, so we will not promise it stays there forever, but it is public while it is there, and anything already copied from it is beyond our reach. Do not put anything private in a recipe or its note.
One case publishes the note itself. If you ask us to mark a recipe verified — the claim that a real purchase went through — and a maintainer approves it, your note is written into that recipe as the evidence for the claim and published with it, word for word. An order number, a receipt, an email address or a name in that note becomes public. Send the maintainer what shows the purchase happened and nothing that identifies who made it.
These submissions are not included in a data export (§11), and deleting your account does not take a published one back (§10): a recipe that went live is kept, with your submitter id replaced and your note dropped, while a submission that never reached the registry is deleted outright.
Activity records
- A per-session audit trail of what your agent did: the merchant host, which API key was used, start and end times, navigations (host only), which kinds of token were filled, denials and their reasons, any challenge the session ran into (its kind, and whether it was handed to you and completed), and the outcome. Nothing the proxy substitutes is ever written to this trail: not a card number, not a security code, not a password, not an identity field.
- Operational telemetry about the sites your agent visits. As your agent navigates, our proxy classifies each navigation (was it blocked, did it hit a challenge, why) and does three things with that. It keeps a running per-merchant tally in memory, which is what we read to see which merchants are breaking. It writes one line per navigation into our server logs, recording the merchant host, the classification and the time. And it sends the merchant host, the category and whether it was blocked to PostHog, the analytics provider named in §8, along with the HTTP status where there was one. A navigation that fails before any response has none, and the field is dropped rather than guessed at. None of the three carries an identifier for you.
- To make that classification, the proxy reads a bounded sample of the page’s visible text, the title and up to a few thousand characters of body text, and passes it to a detector that looks for the signatures of a block wall or a bot check. On a checkout or account page that sample can contain whatever the merchant has rendered, including your own details. It is used and dropped: it is never written to the log line, the audit trail, our database, or anything your agent can read. We would rather describe it than let “the telemetry holds nothing of yours” stand in for “nothing of yours is ever looked at”.
- What is kept from all this is about the site, not you: the tally and the log line carry no account identifier, no session identifier, and nothing you stored. But the log line is a log line, it lands in our hosting provider’s logging infrastructure like any other server log (§8), so “it goes nowhere” would be wrong. It is analytics, and we would rather name it than let “we run no analytics” imply we measure nothing.
- A redacted screenshot of a confirmation page, stored as a session receipt, and a redacted screenshot attached to an approval request when your agent pauses for your sign-off. Receipts are captured under a fail-closed rule, applied at the moment of capture: if the proxy has substituted any sensitive value by then, on that page, an earlier page, or another tab, no receipt is stored. A later confirmation in the same session that captures nothing also clears an earlier one. What this does not do is reach back: a receipt taken from a genuinely clean checkout stays stored even if the same session goes on to fill a token afterwards and never records another outcome.
- A record of each prompt we send you when an agent pauses. An approval request stores the agent card’s name, the merchant host, the amount and your threshold, the reason, the decision and who made it, and the redacted screenshot above. A security-code prompt stores the merchant host and your card’s last four digits, never the code you type. A challenge handoff stores the kind of challenge that was hit (a bank prompt, a one-time code, or a CAPTCHA), the merchant host, the permission to attach to one paused session, and how it ended, completed, failed, or abandoned, kept as its own field even though a failed and an abandoned handoff both show the same expired status, but no challenge response, no one-time code, and no frame of the stream. Each also records which API key the paused fill belonged to for a security-code prompt, and for a challenge the specific proxy session and page being handed over, so those tie back to the agent that raised them. A purchase approval records no key or session, so it identifies the agent card and the merchant but not which key was in use. Each holds the token from the link we email you, which is never returned to your agent. All three links expire within minutes of being sent. The exact window is set per request, because it has to fit inside the paused agent session waiting on you. An approval or security-code token is additionally spent once it is used; a challenge-handoff token is not, and stays usable for its whole window so that a dropped connection can reconnect (§7).
- One caveat on those links: an approval link is written to our operator logs when a purchase is held, and, if our deployment has one configured, is also sent to an operator approval endpoint (§8). Both are deliberate fallbacks, so a held purchase can still be released if email delivery fails. Both mean the link that can approve or deny that charge leaves the email channel: into our internal logs, and possibly to an operations endpoint, until it is used or expires. Security-code and challenge links are not distributed this way.
- Transaction records. Each one holds the merchant host, the amount and currency, a US-dollar conversion, any amount your agent declared, the outcome, and for a refusal the reason. We keep them for spending limits, approvals, and your activity feed. Each row also links the fill back to the funding card, the agent card whose controls governed it (and any cards credited for it), the API key used, and the proxy session, so an entry in your activity feed can be traced to exactly which agent did what.
- Notification records: for every alert we send you, its title and body, the agent card and API key it concerns, whether the email was sent, and a structured copy of the event details, which, depending on the alert, can include the merchant host, your card’s last four digits, the spending threshold that was crossed, the kind of challenge encountered, and the label of the key involved. Also delivery logs for webhooks you configure: the destination URL, the signed body and its signature, plus the attempt record, delivery status, how many attempts were made, the last HTTP status code or network failure reason, when the next retry is due, and created/updated timestamps. One alert carries network detail the others do not: when a session’s traffic leaves our datacenter instead of being relayed through your machine, the untunnelled-egress alert stores the proxy session id, up to five of the destination hosts that were reached, how many hosts and connections were involved, whether any of it was unencrypted, and which route was used instead. A session that used an attested checkout, a path that is not in service today (§5), records a second list the same way, up to five merchant hosts the enclave itself connected to, how many there were, and whether that count was truncated, so a mixed session can retain up to ten hosts across the two lists. Like the rest of your notification history, those rows are retained after account closure (§10).
- While your account is being deleted we hold one row recording that erasure in progress: your account identifier, when the deletion was asked for, which group of records the sweep is currently clearing, and when it last made progress. It exists only between your request and the end of the sweep, which removes it as its final act, and it is what lets the deletion resume rather than start over if it is interrupted. No account that has not asked to be deleted has one.
- If you turn on background alerts, the option that reaches you when the dashboard is closed, we store the push subscription your browser issues: its endpoint URL and the encryption keys needed to deliver to it, plus a coarse device label derived from your browser (“Chrome on macOS”, so you can tell your devices apart in Settings) rather than the raw user-agent string. Each row also keeps delivery bookkeeping: when it was created, when the push service last accepted a message, and how many consecutive failures it has seen, so a permanently broken endpoint can be pruned. The separate in-page desktop-notification toggle stores nothing on our side; it is the device-local flag described in §13.
Technical data
Our servers process the usual request metadata (IP address, user agent, timestamps) in order to serve and secure the service. Our hosting and backend providers keep operational logs on our behalf.
Those logs also carry application identifiers, not just network metadata: every successful agent connection records its proxy session id, and a refused cross-account token attempt records both the account the value belongs to and the account whose session asked for it. They are pseudonymous, no card number, password or code is ever logged, but they can be linked back to your account and session records, and they persist in our hosting provider’s logging stack on its own retention schedule rather than ours.
Our own demo pages
This site hosts two demonstration checkouts, a sample merchant page, and one that uses Stripe’s own card field, so you can watch the token substitution happen. They are toys, but they are ours, and that changes who receives what.
- If an agent completes our sample checkout, the submitted values reach our web server rather than an independent merchant. Whether they are also kept depends on a build switch rather than on whether the build is a production one: when the demo endpoints are enabled, the last submission is held in memory and can be read back, because the end-to-end tests assert on it, and the first name, last name, street and the card’s last four digits are written to the server log. That switch is on by default outside production, and a production build can be started with it on too, our own hermetic transaction tests do exactly that. So do not assume a production URL means nothing is retained. Log entries outlive the in-memory value and are kept by our hosting provider, which is another reason to point a demo at a test card.
- The Stripe demo loads Stripe’s script into the page and tokenizes the card directly with Stripe, so Stripe receives whatever card details are entered there, plus your IP address and user agent. That is separate from our subscription billing (§8) and from a merchant’s own processor (§4).
Point a demo at test cards, not a card you rely on. These two pages are the part of the site most likely to surprise you about where a value ends up.
3. Why we use it, and our lawful basis
Where the GDPR or a comparable law applies, these are the purposes we process your data for and the legal grounds we rely on.
| Purpose | Data used | Lawful basis |
|---|---|---|
| Run your account and vault | Account, identity, addresses, cards, logins | Performance of our contract with you |
| Let your agent complete a checkout or sign-in | Card token, addresses, identity fields, credentials | Performance of our contract, at your instruction |
| Spending limits, approvals, and fraud prevention | Transactions, session audit trail, agent-card settings | Contract, and our legitimate interest in preventing misuse |
| Show you what your agent did | Session audit trail, receipts, transactions | Contract, and your legitimate interest in oversight |
| Alert you to approvals, security codes, and handoffs | Email address, notification content, push subscription | Contract; consent for browser push notifications |
| Billing for paid plans | Email address, plan, Stripe identifiers | Contract, and our legal obligation to keep tax records |
| Screen and publish a registry contribution you send us | The recipe, your note, and the link to your account while it is under review | Contract, since publishing it is what you asked for; and our legitimate interest in a shared registry that stays usable |
| Keep the service secure and available | Technical and operational log data | Our legitimate interest in the security of the service |
| Understand how the product is used, and where it breaks | Pageviews, a random device identifier, declared product events, and the person-less checkout telemetry described in §8 | Our legitimate interest in improving the service. We do not ask for your consent to this, so if you disagree with that assessment you can object (see §11) |
You can withdraw consent for browser push notifications at any time from your browser or the dashboard; that does not affect processing carried out beforehand.
Product analytics runs on our legitimate interest rather than on consent, and we would rather say that plainly than bury it. It is limited deliberately: no profile is built, the identifiers are random rather than derived from your account, and every event name is declared in advance from a fixed list, an undeclared one is dropped whole. The properties are not all declared, though, and we will not pretend otherwise: alongside the declared ones the analytics library attaches its own, the page URL, a coarse device type, screen size, library version and similar, and those are kept, because they are what makes an event interpretable. Each is reduced by what kind of thing it is, and the sensitive-key rules apply to them too. Not anonymous, though, and the word matters here: the identifier is random and never linked to your account, but it is kept in a cookie and in local storage so that repeat visits from the same browser can be counted as one, and §8 discloses the IP address and user agent that travel with it. That is pseudonymous device-level personal data. We should be straight about what the design costs: because the identifier is not tied to an account, we have no way to exclude a particular person from it, there is no account-level opt-out we could honour, and we will not offer one we cannot enforce. Blocking the analytics domain in your browser, or a content blocker, is what actually works today.
4. How agents and the proxy use your data
This is the part of the product that most affects your privacy, so it is worth stating precisely.
- Your agent is issued mock tokens, placeholder strings such as
av_card_1_number_u7x2k. It types those into forms. The agent, its model context, its logs, and anything it sends to its own model provider contain the mock token, not your real card number, security code, or password. There is one caveat we would rather state than imply. The agent can ask how many elements match a selector it chooses, and a page that echoes a filled value back into visible text or an attribute turns that count into an answer about the value, asked enough times. A hostile agent working against such a page is therefore not fully shut out; closing that is tracked as issue #348. The catalog it fetches carries more than the tokens, though, and that travels the same path: your internal account id, which each token also ends with, the labels you gave your addresses and logins, each login’s site reduced to a bare host — and every additional sign-in site you authorised for that login, on the same terms — and whether it has 2FA, and for each card its brand, last four digits and full expiry month and year. Where a card is governed by an agent card in allow-list mode, the catalog also carries that allow-list’s merchant hostnames, the sites you decided that agent may spend at, which says something about you that the other fields do not. Where a card is not narrowed to a list of sites, the catalog says that instead, as a flag: it names no host, and it is the answer to a question the absent hostnames cannot settle, since a card whose agent cards are all paused also has no sites. All of it is what a human needs to tell one saved item from another, and your agent needs the same; but it reaches the agent’s context and its model provider alongside the tokens. - The real values are substituted by our checkout proxy, which drives a browser that runs on our infrastructure. To be clear about what your agent can do with that browser: it does drive it, it navigates, clicks, and types through our SDK, and it can take screenshots, which we redact before returning them. What it cannot do is read the page’s contents directly (script evaluation is refused) or read back any real value the proxy substituted. So your agent controls the session and sees a redacted view of it, and no interface hands it a stored value. One gap we would rather state than gloss, and how it closed: because your agent drives a real keyboard, it could once send editing chords, select-all, copy, paste, to a field the proxy had already filled and carry that value elsewhere without ever reading it back through our API. Once a session has had a real value substituted into it, the proxy refuses every modifier chord that could reach the clipboard, with a stable “clipboard blocked” error. Ordinary typing, Tab, Enter, Escape and the arrow keys are unaffected, and three navigation shortcuts stay allowed by name, Shift+Tab, Alt+Left and Alt+Right, because they cannot copy anything. The rule is deliberately broad rather than a list of blocked shortcuts, because every list we wrote had another spelling past it (issue #252). So we keep your secrets out of your agent’s view and out of its clipboard, in a session it otherwise fully controls. The proxy resolves a mock token to a real value only after our backend has authorised that specific fill. Every kind of value is checked against a live, non-revoked API key that belongs to your account, with one exception, a challenge-handoff page whose viewer is already CONNECTED when the key is revoked (§12, issue #212). A security-code prompt is not an exception: the card is re-authorised after the code arrives, so revocation stops it. Login fills are additionally locked to the site the credential was saved for. A card-number fill is additionally checked against merchant policy, spending limits, and any approval you require, but only when an agent card governs that card; a card filled directly, with no agent card linked to it, is subject only to your plan’s account-wide cap on the NUMBER of payments in a rolling 30-day window (2, 10 or 30 depending on tier), a count, not an amount, so it bounds how often such a card can be used and never how much it spends. If you want a card governed, put it behind an agent card.
- A security code on its own is checked, but not counted. When a saved-card checkout asks only for the security code and never for the number, that fill still runs the key, account, pause and merchant checks, and it can still pause for your approval, the code is as sensitive as the number, so it is gated the same way. What it does not do is consume your spending limits or record a transaction: those happen on the card-number fill, so that a card and its code together count once rather than twice. The practical consequence is that a code-only checkout can be stopped by a pause, an allowlist, or an approval, but it is not bounded by your spending limits.
- An agent can also cause data to be written, not just read. If you grant its key the power to create a login in your vault, which is off by default and switched on one key at a time, the agent can sign you up for an account on a site and save the result. It does not choose the password and never receives it. We generate the password in our backend, encrypt it, and hand the agent only a mock token, so the value exists in our storage and in the site’s, and in no model context anywhere. It is not displayed in the dashboard either, because nothing in the product reads it back out. The agent supplies the site and the username, which are stored readable as above; it cannot add extra sign-in hosts, that stays something only you can do. A key you scoped to specific logins can still sign you up, and the login it creates is added to that key’s own list — otherwise it would have made a credential it could never use. Only that one: signing you up never puts anything else on that list, and what else is on it stays yours to set from your dashboard. The list stops at fifty entries, after which its signups are refused. Every such write is capped per day per account, emailed to you, and written to your activity feed.
- Two-factor setup is the one place a secret travels the other way. If an agent is completing a site’s authenticator setup, the site has already printed the seed on a page our proxy is rendering. The agent tells the proxy which element holds it, never the value; the proxy reads it, sends it to our backend to be encrypted, and returns a mock token. So on this one path the seed does pass through the proxy, in that direction only, and only for a seed the site is showing on screen at that moment. It is not read back to the agent, and it is not sent back out to the proxy afterwards (§6). From the moment it is captured, that session is treated as having handled a real value: screenshots are blacked out for the rest of it, and the agent can no longer list the page’s frames or read their addresses. The selector-counting the agent needs to work a page at all still answers, as §4 describes.
- The merchant or login site receives the real value, because that is the point: a real purchase needs a real card number, and a real sign-in needs a real password. Once submitted, that data is governed by the receiving site’s own privacy policy, not this one.
- Often the merchant’s payment processor receives the card, not the merchant. Many checkouts embed card fields hosted by a payment provider, Stripe, Adyen, or similar, in a frame belonging to that provider, and our proxy fills those frames directly. Your card number, its expiry, and the security code you enter for that purchase then go to that provider under their privacy policy and the merchant’s agreement with them. We have no relationship with them and do not choose them; the merchant does. (This is separate from the Stripe entry in §8, which covers only our own subscription billing.)
- The proxy browses whichever sites your agent directs it to, and those sites see whatever your agent submits.
- By default they see the agent machine’s IP address, not ours. When an agent runs through our SDK, the proxy relays the browser’s network traffic back to the machine running your agent and dials the merchant from there, so the merchant sees that machine’s public address. If you run the agent on your own computer that is your home network, typically a residential IP, which is deliberate: a datacenter IP is a strong bot signal that blocks many checkouts. If you run it on a cloud VM, a CI runner, or a hosted agent platform, the merchant sees that provider’s address instead, and it may well be a datacenter one. In either case the address of whichever machine runs your agent is disclosed to every site it visits. One path is an exception. A default session prefers the relay rather than requiring it, and an attested checkout runs the merchant page in a separate hardened browser that cannot use the relay the way your own agent’s browser does, so on that path the merchant sees our datacenter address, not yours, even with default settings. Only a session that requires the relay (
tunnel: true) is refused rather than allowed to proceed that way. That path is not in service today and is refused before it runs (§5); it is described here so the disclosure is already right if we do turn it on. The hosts it reached are recorded and disclosed to you (§2). We have since built a relay that carries the hardened browser’s traffic back through your machine too, which would remove this exception, but it is not enabled in any environment, so the datacenter address above is what would happen today. Passtunnel: trueif you need the relay to be mandatory rather than preferred. For anhttps://site, which is every real checkout, the relayed bytes are TLS-encrypted and your machine cannot read them. On a plainhttp://page we now refuse the fill: no card number, security code, password or identity value is substituted into a page that is not TLS-protected, and the agent is told the page is insecure. Such a page is also no longer relayed through your machine, plaintext traffic leaves from our datacenter instead, so it cannot put anything of yours on your own network in the clear. You can turn the relay off (tunnel: falseorSELF_AGENT_PAY_TUNNEL=0), in which case sites see our proxy’s datacenter IP instead; the same happens automatically if the relay cannot be established. - Unrelayed traffic may go out through a commercial proxy provider. A datacenter address is itself a bot signal, so the proxy can be configured to route egress that is not going through your machine via a third-party upstream proxy instead. Where a deployment is set up that way, that provider carries the browser’s traffic, sees every host it dials, and is the address the merchant sees rather than ours. It is a per-deployment setting rather than something every installation does, which is how §8 lists it. The hosts reached are recorded and disclosed to you either way (§2), and the same TLS reasoning applies: on an
https://checkout the provider carries encrypted bytes, and a plainhttp://page has its fill refused before anything of yours is typed into it.
5. Card data specifically
Card numbers are held by Basis Theory, a PCI-DSS Level 1 certified tokenization vault, and not in our own database. When you add a card, the number is typed into an input hosted by Basis Theory inside your browser and sent directly to them; we receive back an opaque token reference and store that. The write path checks that the reference is a vault token identifier and that the last four digits are four digits, so a card added through the normal flow cannot put its number in the token field.
That is a statement about the normal flow, and we will not overstate it into a guarantee the database enforces. The record also carries free-text fields, the cardholder name and the brand, and those accept any string. A caller who invokes our card-creation API directly, or a client that maps its fields wrongly, could therefore put a card number in one of them and we would store it. Nothing in the product does that, and the fields are not treated as card data anywhere; but the protection is the shape of the normal flow rather than a rule enforced on every field, and you should know which of the two it is.
If adding a card fails after that step, a validation error, or a network failure while saving the rest of the record, the card is already in the vault and we end up with no reference to it. Nothing in our system then points at it, which also means our own deletion tooling cannot find it. Tell us if a card save failed and you want it removed; until then it sits in the vault unreferenced. Cleaning those up automatically is tracked as issue #214.
For completeness: our database schema still defines columns that held encrypted card numbers, and one that held encrypted security codes, under the design that preceded the vault. No code writes any of them any more, and scheduled jobs purge the remaining pre-vault rows so the columns can be dropped altogether. Until that finishes, we would rather tell you the columns exist than claim a schema that cannot hold a card number.
Where the number is revealed. To type your card into a merchant’s checkout form, something has to turn the token back into digits. Today, wherever card filling works at all, that happens inside our checkout proxy process, there is no configuration in which some other component reveals it: the proxy calls the vault, holds the number in memory, and types it into the merchant page. It is not written to our database, our logs, or the session audit trail. A deployment that is not configured to fill cards refuses the fill outright, before any number is retrieved; that is the only other outcome.
Where that happens matters. The proxy runs on Confidential GKE nodes, which keep a machine’s memory encrypted while it is in use by the processor. So for the seconds your card number is in the proxy’s memory, an attacker with access to the host, or to the physical RAM, reads ciphertext rather than your card number. That is hardware doing the work, not a promise about our code.
Our software also carries a separate attested checkout path, described in §4, whose isolated enclave is not deployed. Were it deployed, it would take the vault token inside, reveal the number there and type it from within, so that not even we can see it. As things stand every attested checkout is refused before any card changes hands, and the paragraph above is still what happens to your card. When that changes we will say so on this page.
How long it stays in memory. Not just the moment of typing: once a value has been filled, the proxy keeps a copy in that session’s memory until the session ends, so it can recognise and blank out the value if it would otherwise appear in a screenshot or an error message returned to your agent. The retention is what makes the redaction possible, the proxy cannot scrub a value it no longer holds, but it does mean the plaintext lives in proxy memory for the session, not for a single fill. It is never persisted anywhere. The same applies to a password or security code the proxy fills.
That in-proxy reveal is switched on only where the vault’s private key has been provisioned for the deployment. Without it, the proxy refuses every card fill outright rather than falling back to anything weaker, so your card number is never revealed anywhere, and your agent cannot complete a card checkout at all. The two are deliberately tied together, so a half-configured deployment cannot exist.
Our marketing FAQ no longer claims an “attested Confidential Space enclave”: that architecture was retired, and the reveal now happens on Confidential Compute hardware that is a genuine trusted execution environment but is not attested to anyone. One claim there is still wrong, though, the FAQ says your card number “never touches our servers”, and the paragraph above describes what actually happens. Where the two conflict, this policy is the accurate one. Correcting that sentence is tracked as issue #194.
Vault status. Our vault account with Basis Theory is currently an evaluation tenant, not a production plan, so a card added here is stored in their test tenant. Use test cards only.
Whose certification is whose. Basis Theory holds PCI-DSS Level 1 certification. That certification is theirs and it describes their vault. It is not a certification of Agent Vault, and we make no PCI, SOC 2 or ISO claim about ourselves.
6. Site credentials specifically
We store the passwords and one-time-code seeds for the sites you connect. There is no way for an agent to sign in on your behalf otherwise, and we would rather say so plainly than bury it.
- Passwords and TOTP seeds are encrypted at rest with AES-256-GCM. Records written under the current scheme use a per-record data encryption key, itself encrypted (wrapped) by a key-encryption key, and their ciphertext is cryptographically bound to your account so it cannot be decrypted in the context of another user.
- Older records may not have both of those properties. Credentials saved before the per-record-key and account-binding changes are still readable under the previous shared-key scheme, and ciphertext written before account-binding decrypts without that binding check. A record is re-keyed only when the secret itself is re-entered, editing a login’s site, username or note leaves the existing ciphertext exactly as it was. So retyping the password (and the one-time-code seed, if you use one) is the way to move an old login onto the current scheme; changing anything else will not do it.
- The key-encryption key is held by us, as an environment secret in our backend. It is not in a hardware security module or a third-party key-management service today. Our deployment prefers a dedicated key-encryption key, but where one has not been provisioned it falls back to the same secret used for legacy data, so the separation between the two is a deployment property, not something this page can promise. A sufficiently deep compromise of our backend environment could reach stored credentials, and we are not going to describe that risk as eliminated.
- Usernames, site URLs, and your notes are stored in readable form so the dashboard can list your connected sites.
- Decryption happens only in our backend, and only for a fill your agent is authorised to perform. What leaves the backend depends on the field. A password is passed to the proxy as plaintext for that fill. A two-factor seed is not: it is decrypted inside the backend, used there to derive the current six-digit code, and only that short-lived code is sent on, so we never send a stored seed back out to the proxy and a proxy session can never generate a future code from one we hold. There is one path in the other direction, and it is the only one. When an agent completes a site’s authenticator setup for you (§4), the site itself prints the seed on the page the proxy is already rendering; the proxy reads it there and passes it to us to encrypt. So a seed can travel proxy → backend at the moment it is created, and never backend → proxy afterwards. We would rather draw that arrow than let the shorter sentence stand. As with a card number (§5), whatever value does arrive is kept in that session’s memory until the session ends so it can be redacted out of screenshots and error text; it is never persisted. Neither is returned to the agent or written to the audit trail.
A password we generated, we cannot show you. Where an agent created the login (§4), the password was produced in the backend and written into encrypted storage without ever being rendered, so no screen in the product displays it and no support request can produce it. A data export gives you the same fields as any other login, telling you the account exists and that its password is one nobody has read. If you want a password you can type yourself, reset it on the site and save the new one.
Many sites’ terms restrict credential sharing and automated access. Connecting a site, and letting an agent connect one for you, is your decision and your responsibility, see the Terms of Service.
7. Session records and the live screencast
Every agent session produces an audit trail so you can see what was done in your name. It records the merchant host, the API key used, timings, which token kinds were filled, denials and their reasons, and the outcome, never a substituted value.
When a site raises a challenge the agent cannot pass (a CAPTCHA, a two-factor prompt), the session can be handed off to you. We email you a link; opening it streams a live video of the proxy’s browser to that page and relays your clicks and keystrokes back, so you can finish the step yourself.
- The link is the credential. The handoff page is deliberately outside the signed-in area, because you are usually on a phone where you are not logged in, so it authenticates on the token in the link alone, and does not additionally check that you are signed in. The token is not spent by being used once: it stays valid until the handoff expires or settles, so that a dropped mobile connection can reconnect to the same session. For that whole window, anyone holding the link can attach and take over the live checkout. Treat a handoff email like a password reset email.
- Those frames are unredacted pixels of the page in front of you. They are sent only to the one attached viewer socket, drawn to a canvas, and replaced by the next frame.
- No frame is stored. Screencast frames never reach our database, the audit trail, a receipt, our logs, or anything the agent can read.
- The token is issued for one specific session belonging to your account, expires, and admits only one viewer at a time. The proxy re-checks the session, page, and owning account before it attaches.
8. Sub-processors and other recipients
We do not sell your personal information and we do not share it for advertising. We use the following providers to run the service. Our own infrastructure runs in the United States. Several of these providers operate globally distributed networks and may process data at edge locations outside the United States; each provider that acts as our sub-processor is bound to us by a data processing agreement. That is not true of every recipient in the table below, and we will not imply it is: a merchant or login site your agent visits, the merchant’s own payment processor, and a webhook endpoint you configure are not our sub-processors, we have no agreement with them about your data, and their rows say so. We will tell you the processing locations recorded for any provider we do contract with, on request at legal@self.xyz.
| Provider | Role | Data it receives |
|---|---|---|
| PostHog | Product analytics for this website and for the checkout proxy | From the website: pageviews as route templates, a small number of product events, a random device identifier, and the IP address and user agent that any web request carries. From the proxy, separately: that a checkout session started and ended and which egress mode it used, that a navigation was blocked and to which host, how a security-code prompt or a challenge handoff settled, and that a legacy token was refused. Only the session start and end carry a random per-session handle; the others carry a handle generated once per proxy process, which means they can be correlated with each other across different people’s sessions handled by the same process. Neither handle is derived from your account. Both streams are person-less: no profile is built, and neither the device identifier nor the session handle is tied to your account |
| WorkOS | Authentication and session management | Email address, authentication identifiers and events; and, because signing in sends your browser to them, your IP address and user agent |
| Basis Theory | Card vault (evaluation tenant) | Your full card number and expiry, sent directly from your browser; the account identifier stamped on the token; and, because your browser makes the request, your IP address and user agent |
| Stripe | Subscription billing and hosted billing portal, plus our Stripe demo page | Email address, account identifier, and the subscription and payment details you enter on Stripe’s pages. Subscribing and opening the billing portal send you to a page Stripe hosts, so Stripe also receives your IP address, user agent and request timing on those visits, not only on our Stripe demo (§2), where it additionally receives whatever card details are entered |
| Resend | Transactional email | Recipient email address, notification subject and body, and bearer action links, for approvals, security-code prompts, and challenge handoffs. A handoff link opens the live view of your checkout (§7), so Resend handles a credential, not just a notification |
| Convex | Application backend and database | Everything described in §2 that we store |
| Google Cloud (GKE, Secret Manager, Cloud KMS) | Hosting, secret storage, key management | Data in transit through our services; encryption keys; operational logs |
| Vercel | Hosting for the website and its server routes, including the preview environments this policy describes | Your request metadata (IP address, user agent, the paths you request) and anything submitted to a server route it hosts, such as the demo checkout |
| Sentry | Crash reporting for the checkout proxy, and only where the operator of a deployment has configured it — it is off unless a Sentry DSN is set | A diagnostic event: the exception type and its stack frames, the proxy session handle, operational tags, and what the SDK attaches to any event — a request URL, breadcrumbs, runtime context, and, if the deployment turns on tracing, transaction spans. No identity is ever attached, and the integration that would collect request headers is switched off, as are request bodies, cookies and the environment. Everything that remains is scrubbed against the values that session resolved, and the message is withheld entirely when it cannot be proven free of them, so no card number, security code, password or one-time code is sent |
| A commercial proxy provider | Network egress for browsing that is not relayed through your own machine, and only where the operator of a deployment has configured one — there is none unless an upstream proxy is set | The browser’s network traffic and the hosts it dials, plus the fact that a session ran. On an https:// site the bytes are TLS-encrypted and the provider sees the destination rather than the contents |
| Anthropic | Automated screening of checkout recipes contributed through POST /v1/recipes, only if you contribute one | The recipe you submitted and any free-text note you sent with it |
| Google Fonts | Web fonts, loaded on every page including pages you can view signed out | Your IP address and browser user agent when the page loads |
| Your browser vendor’s push service | Push delivery (Google, Mozilla, Microsoft, or Apple, depending on your browser; Safari and iOS route through Apple), only if you enable it | Your push endpoint and delivery timing. Notification content is encrypted to your device and is not readable by the push service |
| Merchant and login sites you direct an agent to | Not our sub-processors, independent recipients | Whatever your agent submits, including real card and credential values |
| The merchant’s payment processor | Not our sub-processor, chosen by the merchant, e.g. Stripe or Adyen (§4) | Your card number and expiry, and the security code you enter for that purchase, when the checkout embeds their card fields |
| A webhook endpoint you configure | Not our sub-processor, an integration you choose, on paid plans; only if you set one up | A signed JSON payload for each notified event: its id and type, the agent card it concerns, and the non-link event details. Action links are not included |
| An operator approval endpoint, if our deployment configures one | Not our sub-processor and not chosen by you, an optional operations integration on our side, off unless configured | When a purchase is held: your account identifier, the name you gave the agent card, the merchant, the amount and your threshold, the expiry, and the approval link itself, which can release or deny that charge |
We also fetch daily currency reference rates from a public exchange-rate API in order to check foreign-currency totals against your limits. That request is made by our server and contains no personal data.
We may disclose data where we are legally required to, or to establish or defend legal claims. If we are ever involved in a merger or acquisition, your data may transfer as part of that transaction; we would tell you before it became subject to a different policy.
9. International transfers
Our infrastructure runs in the United States, and the providers in §8 are US-headquartered, though several operate globally distributed networks. If you are in the European Economic Area, the United Kingdom, or Switzerland, using Agent Vault means your data is transferred to the United States.
Where a provider transfers personal data out of the EEA, the UK or Switzerland, we rely on the Standard Contractual Clauses adopted by the European Commission, together with the UK Addendum where the transfer is from the UK, except where that provider is certified under the EU – US Data Privacy Framework and its UK and Swiss extensions, in which case we rely on that certification. Ask us at legal@self.xyz for the mechanism recorded for a particular provider and a copy of the relevant safeguards.
10. Retention, and how far deletion reaches
This section describes what our system does today, including where it falls short of what a reader would reasonably expect. Each shortfall names its tracking issue.
Automatic deletion that does happen
- The session audit trail, session rows, their events, and their receipts, is purged 30 days after a session starts, on every plan. An hourly job does this.
- The card security code is not written down for any purchase made now, so there is nothing to expire, apart from the legacy column still being purged (§5).
- Screencast frames from a handoff are never stored, so there is nothing to expire.
Data kept until you delete it
Your account, addresses, card references, logins, and agent cards are kept for as long as your account exists. Approval requests, security-code prompts, challenge prompts, webhook delivery logs, and push subscriptions currently have no scheduled expiry and are otherwise kept until account deletion.
Two exceptions, both about push subscriptions. One is in your favour: a subscription is deleted as soon as the browser’s push service tells us it is dead (an HTTP 404 or 410 when we try to send), so a subscription for a browser you no longer use gets cleaned up automatically, just not on a timer. The other is a cap: we keep at most 20 push subscriptions per account, and registering one beyond that deletes your oldest to make room. Every other row in the list above waits for account deletion.
To be clear about what that means for a prompt: the link we emailed you stops working within minutes (§2), but the record of the prompt is not deleted on any schedule. Nothing expires it. What that record holds differs by prompt, and only the first is a record of a decision about money:
- An approval request keeps the agent card, the merchant, the amount being approved, your decision, and, where one was taken, a screenshot of the checkout.
- A security-code prompt keeps the card’s last four digits, the merchant, and the status and timing. No amount, no decision, no screenshot, and never the code itself.
- A challenge handoff keeps which kind of wall was hit, the session and page it paused, the status and timing, and how it ended — the stored status collapses a failed and an abandoned handoff into the same expired, and the outcome beside it keeps the two apart. No amount, no decision, no screenshot.
Your notification history, your transaction history, and your API key records have no automatic expiry either, and, unlike the records above, they are not removed by account deletion. An API key is marked revoked and its row kept, not erased. See below.
There is no defined retention period for any of the records in this subsection. They are kept indefinitely, until either you delete your account (for the first group) or we adopt a retention policy and implement the job to enforce it. We are not going to state a number we do not enforce. Setting that policy is part of issue #106; the absence of any cap on the transaction table is separately tracked as issue #175.
What account deletion removes
Deleting your account removes your cards’ records, logins, addresses, agent cards, every approval, security-code and challenge prompt — settled ones as much as pending, so the merchant, amount, decision and any screenshot those hold go with them — webhook delivery logs, push subscriptions, and your user record, and revokes every API key so in-flight agent sessions immediately stop resolving tokens.
It does not, however, terminate a proxy session that is already running. Revocation stops that session obtaining any further real value the moment it lands, but the browser it already has stays up until the session ends by itself or hits its one-hour ceiling, and whatever was already typed into the page can still be submitted. Deletion is therefore not an emergency stop: stop a running agent before closing your account.
What account deletion leaves behind
A published contribution outlives the account, without the link to you. A contributed recipe may already be in a registry other people are using, and that is the only reason any of this is kept. So deletion splits the submission records of §2 in two, and the line between them is whether the recipe went live — not whether a maintainer has finished with it. A submission whose recipe is live, approved or merged is retained, with its submitter id rewritten to a sentinel value no account can ever hold and the free-text note you wrote for a maintainer cleared, because that note was never public. That covers a live verification request whose claim a maintainer is still weighing: the recipe is out there, so the row stays and your note does not. Every submission whose recipe never went live is deleted outright. A recipe already published is public regardless, and cannot be recalled from anyone who has copied it.
We cannot confirm that your card number leaves the vault when you delete your account. Deletion does queue an instruction to destroy the stored card, and that instruction now goes to Basis Theory directly rather than through a separate service that was never deployed. What it still needs is a delete-only vault key. Our deploy installs that key when we have provisioned it and takes it away when we withdraw it; we have not provisioned one, so no deployment holds a working key today. Without the key, every attempt stops at the check for it, retries on a ladder, and eventually gives up. It is not silently skipped, but nor is the card provably destroyed. Treat your card as still stored at Basis Theory until it is removed by hand. We would rather tell you that than tell you we delete your card data and be wrong. If you want the card destroyed, email legal@self.xyz and we will action it manually.
Each abandoned attempt is written to our operational logs with the vault’s identifier for that card and your account id, deliberately, so an operator can find an orphaned card later and destroy it. It does mean that after deletion a reference to the vaulted card survives in our logs even though the database row is gone. That reference is what makes the manual cleanup above possible.
Deleting your account winds your billing down first. The deletion lists your live Stripe subscriptions and cancels them before anything is erased, and it refuses to delete the account at all unless it can confirm they are gone. A deletion that succeeds has therefore stopped the billing it could see.
The qualifier is deliberate. The last thing checked before the point of no return is a fresh list of your subscriptions at Stripe, and a subscription can still begin between that list and the erasure committing. Nothing on our side can prevent that; what catches it is the deletion of your Stripe customer described below, because deleting a customer cancels what is on it. If that job exhausts its retries, a subscription started in those last seconds can survive the account. It is a narrow case and we have not seen it, but we will not tell you closure ends billing in every case when this one exists. If anything is billed after your account is gone, email legal@self.xyz and we will cancel it and refund what was charged.
A deletion that fails keeps your account and deletes none of your data, but it is not always a no-op at Stripe. The wind-down runs first, so a cancellation or an expired checkout session that had already gone through is not undone by the failure. The error tells you which of those had landed, and says so explicitly when contact with Stripe was lost mid-request and it cannot tell either way, so you can check your billing there rather than assume.
The case it refuses is a subscription that appears during the deletion. A Customer Portal tab you still have open answers to Stripe rather than to us, so a new subscription can start after the wind-down and before the erasure; the deletion stops rather than purging over it, and retrying cancels the new one too.
This is not limited to paid plans. The moment you start a checkout we create a Stripe Customer carrying your email and account id, plus a Checkout Session, so anyone who has ever opened the upgrade flow has records at Stripe. Deletion schedules that customer for deletion at Stripe, in the same transaction that removes our own pointer to it, and retries over about two and a half hours if Stripe cannot be reached.
If every one of those attempts fails, the customer stays at Stripe and the failure is written to our operational logs for an operator to clear by hand. Nothing re-queues it after that, and you are not told. If you want to be certain the record is gone, email legal@self.xyz and we will confirm it.
The rest is erased in the background, not kept. Deletion removes the records above in the moment, then schedules a sweep that drains everything a larger account leaves over: the remaining approval, security-code and challenge prompts and webhook delivery logs, then transaction history and notification history, then agent session rows, their events and their receipts, and finally the revoked API-key rows. The sweep works in phases and resumes where it stopped, and a daily job restarts one that has stalled. What lets it resume is the record of an erasure in progress (§2); that record is deleted last, once nothing anywhere still refers to your account, and it is the only trace of you that outlives the first pass. It is not instantaneous: on a large account it can still be running for a while after the dashboard says the account is gone.
A receipt is a redacted screenshot of a merchant confirmation page (§2), kept as a record of its own against the session it belongs to rather than on the session row. Both the deletion sweep and the 30-day session purge remove those records in their own right, and before the session they belong to, so whichever reaches one first is what removes it.
Deletion does not remove your identity record at our authentication provider, which is what still lets you sign in. Signing in again does create a new account, and that account is empty: account ids are minted at random rather than derived from your sign-in, and the id being erased is reserved until its sweep finishes, so nothing from the deleted account can end up indexed under the new one.
A notification email already queued when you delete can still be sent afterwards: the queued job carries the recipient and the message, and deletion does not cancel it. Any action link inside such an email is already dead, because the record it pointed at was deleted, but the email itself may still arrive.
Two things also survive on purpose and are described above: your registry contributions, with the link to you rewritten away, and the log reference that lets an operator find and destroy an orphaned vaulted card. If you want records about you removed sooner than the sweep manages, ask us and we will action it by hand.
11. Your rights and how to exercise them
Depending on where you live you may have the right to access your data, receive a portable copy, correct it, delete it, restrict or object to processing, and complain to a supervisory authority. We do not discriminate against you for exercising any of them.
Correction and day-to-day control
In the dashboard you can see a listing of what you have stored, your addresses, logins, cards and agent cards, and delete any of them; addresses, logins and agent cards can also be edited in place. Cards cannot: correcting one means deleting it and adding it again, as the paragraph below explains. Two things are kept but not listed there: a settled prompt (an approval you answered, a security-code or challenge request that was used or expired) stays in our records until you close the account, while the dashboard shows only the ones still waiting on you. Ask us under §11 and we will hand over the rest.
Your stored secrets are write-only there, deliberately: when the dashboard lists what you have saved it never receives a password, a TOTP seed, or a card’s vault reference, only whether a password and a code are set, and the card’s last four digits, brand and expiry. You can replace a secret but not read one back. If you need to know a value you stored, we cannot show it to you; recover it from the site it belongs to.
The one moment that differs is adding a card. Your browser is what talks to the vault, so it necessarily handles the reference the vault returns and passes it to us on the way in. After that it is never sent back out to the browser again.
Cards have no edit form, correcting one means deleting it and adding it again. API keys can be revoked in the dashboard; a revoked key stops working immediately, apart from a challenge-handoff page whose viewer is already connected at that moment (§12, issue #212), and its record is retained rather than erased. Deleting an individual card removes our reference to it, but see the vault caveat in §10, which applies to deleting a single card just as it does to deleting your account.
Export and erasure
Account export and account erasure exist as backend operations, but there is no button for either in the dashboard, and, being worth saying plainly, no support tooling behind them either. Both are self-service functions that authenticate as you; there is no administrative version we can run on your behalf. Fulfilling an emailed request today means an operator doing it by hand against the database.
That does not reduce your rights, and we will honour a request to legal@self.xyz within 30 days. It does mean the process is manual rather than a supported flow, and we would rather tell you that than imply a button exists. Building the real path is issue #106.
The export currently returns your profile (account identifier, email, first and last name, phone, plan, sign-up and onboarding timestamps, notification preferences), your addresses, your logins (label, site, any additional sign-in hosts, note, username, whether a password and a TOTP seed are set — never the secrets themselves — when the login was created, and, for one an agent made for you, which API key made it, whether the password was generated rather than given to us, and when and on what host an agent enrolled its 2FA), your cards’ non-secret metadata, your agent cards, and your API key records without the keys.
Those API key records are selected fields rather than the whole row: you get the label, prefix, revocation state and reason, the cards and logins the key is allowed to reach, whether you let the key open new accounts, and the created and last-used timestamps. The row also carries security counters we do not export, how many calls the key has made in the current rate-limit window, and how many times it has been refused for reaching across accounts, each with the window they are measured over. Ask us under §11 if you want them.
It does not include several things we nonetheless hold, so an export is not a complete picture of your account: your notification history and its stored event details (§2), webhook delivery logs, settled approval and prompt records, your push subscriptions with their device labels and delivery bookkeeping, and your registry contributions, the checkout recipes and maintainer notes described in §2. Deleting your account removes the webhook logs, the settled prompts and the push subscriptions, but not the notification history, which is deliberately retained (§10). Ask us under §11 for anything the export leaves out.
One detail worth knowing before you rely on it: wherever the export contains a URL, a login’s site, a merchant entry on an agent card, a webhook address, it gives you the host only. Any path, query string, fragment, or embedded credentials you saved are stripped, so the export will not always match the value you typed. Ask us if you need the exact stored strings.
It does not currently include your transaction history; your session audit trail and receipts; your notification history; your push-subscription records (the device endpoint and browser keys stored when you enable notifications); your approval, security-code, and challenge prompt records; your webhook delivery logs (destination URL, signed body, signature); the signing secret an agent card uses to sign your webhook calls, which the dashboard does show you; the billing identifiers Stripe issued for your account; the subject identifier our authentication provider uses for you, which is the link between your sign-in and your Agent Vault account; or the record of an erasure in progress (§2), which by definition only exists once you have asked us to delete the account an export would have been for. Nor does it include the operational request logs described in §2: the IP addresses, user agents and timestamps that our hosting and backend providers hold on our behalf, rather than in your account record. If you want any of those, ask and we will provide them.
Complaints
If you are in the EEA, the UK, or Switzerland you may complain to your local data protection authority. We would appreciate the chance to address it first at legal@self.xyz.
12. Security measures
What follows is what our system actually does. We hold no security certification, and we do not claim one.
- Your secrets are never returned to the agent. It works with mock tokens; the substitution happens in a browser process your agent drives but cannot read out of, script evaluation is refused, and its screenshots are redacted. That is a narrower statement than “the agent never has them”, and deliberately so: the agent controls the session. The keyboard route out is closed, though, once a real value has been substituted, every clipboard-capable chord is refused (only Shift+Tab and Alt+Left/Right survive, which move focus and copy nothing), so a value it cannot read cannot be copied out with a shortcut either. §4 describes the path and the guard (issue #252).
- Card numbers are held in a PCI-DSS Level 1 vault (Basis Theory’s certification, not ours), tokenized directly from your browser. A card added through the normal flow cannot land in our database, the write path takes a vault token id, never a number, though it does store the non-secret metadata in §2 alongside it, and as §5 explains those descriptive fields accept any text, so the protection is the shape of that flow rather than a rule enforced on every field. Encrypted numbers from the pre-vault design may still sit in legacy rows until the purge job clears them (§5).
- Security codes are not stored, they are entered by you per purchase and held only in the proxy’s memory, for that session, on the same redaction-driven terms as a card number (§5). As with card numbers, a legacy column is still being purged (§5).
- Credentials are encrypted at rest. Records written under the current scheme use a per-record key wrapped by a key-encryption key and are bound to your account, so they cannot be decrypted for someone else. Older records may have neither property until they are re-keyed (§6).
- Every fill is authorised server-side and fails closed. Card numbers, security codes, passwords, one-time codes, identity fields and addresses are all released the same way: only to a live, non-revoked API key bound to your account. So revoking a key stops resolution immediately.
- One narrow exception, worth knowing: a challenge handoff whose viewer was already attached when you revoke the key keeps driving the paused session, because that live connection is not re-checked against the key while it lasts. It is narrower than we used to state: a handoff link that has not been opened yet, and an attached page whose connection drops and tries to reconnect, are both refused, attaching reloads the key and stops on a revoked one. So revocation does close the handoff route, except for a viewer already holding the connection. If you revoke a key to stop something in progress, do not then complete an outstanding handoff. Making revocation settle open prompts is tracked as issue #212.
- A security-code prompt is not an exception. Entering the code after you revoke the key does not get it to the checkout: the card is re-authorised once the code arrives and before it is released, so a revoked key, a paused card, or a changed merchant policy stops it there. We previously listed this alongside the handoff, which overstated how weak revocation is.
- The spending controls do not cover everything, and we would rather say so. An agent card’s merchant allowlist, pause, and approval threshold govern card and security-code fills. Its spending limits are narrower still: they bind card-number fills only, so a checkout asking for the security code alone is never counted against them (§4). Pausing an agent card does not stop that agent from resolving a saved password, a one-time code, an identity field, or an address, those are governed by key scope, account binding, and (for logins) the site lock, but not by pause. To stop everything at once, revoke the API key. Extending the pause control to the other secret types is tracked as issue #181.
- API keys are stored hashed and can be revoked instantly. Scoping is optional: you may restrict a key to chosen cards and logins, but a key created with the scope controls left on “All” has access to all of them. Identity fields and addresses are not per-key scopable at all, any live key on your account can resolve them. Scope your keys deliberately; the default is not the narrow one.
- The audit trail is built from token kinds, not values, and the storage boundary rejects card-shaped strings and unknown token kinds. It is not, however, structurally incapable of holding a secret: the host, field, reason and detail slots accept free text, so the guarantee rests on what our own service writes into them rather than on a rule the database enforces.
- No advertising, and no cross-site tracking anywhere. We do not sell your data, we do not share it with advertisers, and nothing here follows you to other sites. We do run product analytics on our own website and in the checkout proxy, through PostHog. §2 and §13 say exactly what is sent, and §8 gives PostHog its place in the provider list.
- Traffic between you, our services, and our providers is encrypted in transit with TLS. Traffic to a site your agent visits is encrypted when that site is
https://, which is every real checkout, and a plainhttp://page is refused rather than filled, so a real value is never typed into a document that is not TLS-protected (§4).
What we are not claiming. The card number is revealed inside our proxy process during a fill (§5). That process runs on Confidential Compute hardware, so its memory is encrypted in use, but it is not attested to anyone: nothing proves to you which code was running. The separate enclave of the attested-checkout path is a different thing again and is not deployed. Do not read the FAQ’s mention of an enclave as either of the stronger claims. The key-encryption key protecting your credentials is held by us. Our vault account is an evaluation tenant. No method of storage or transmission is perfectly secure. Do not put data into this that you cannot afford to lose.
13. Cookies and local storage
We set two kinds of cookie: one to sign you in, one to count visits. For signing in, our authentication provider sets an encrypted session cookie, and during sign-in and token refresh a short-lived workos-access-token cookie may also be set, which the app reads once and then immediately clears. Both are strictly necessary for the service to work. The second kind is the analytics cookie described in the next paragraph.
We set no advertising or cross-site tracking cookies, and nothing we set follows you to another website. Our product analytics does set one first-party cookie holding a random device identifier, so that two pageviews in the same visit are not counted as two visitors. It is not shared across sub-domains, it is not tied to a profile, and it is described in full below.
No third party sets a cookie through our site, with one exception you have to visit deliberately: our Stripe demonstration page (/stripe-demo) loads Stripe’s own script from js.stripe.com and mounts a card field that Stripe, not we, renders. Stripe sets its own cookies there for fraud and device recognition, governed by Stripe’s privacy policy rather than this one. The card details you type there go to Stripe, never to us; what comes back to the page is Stripe’s token metadata, a token id, the card brand and the last four digits, which the demo simply displays. We do not send it anywhere or store it: it lives in the page until you navigate away. No other page on this site loads that script, so if you never open the demo, no third-party cookie is ever set.
Your browser’s local storage holds a handful of values, and we will not put a single number on it because the count depends on your history here. Three are display preferences with no personal content: your light/dark mode (av_mode), your visual style (av_aesthetic), and your accent colour (av_accent). If you used this site before those keys were renamed you may still be carrying their predecessors sap_mode, sap_aesthetic and sap_accent. We read them so your theme survives the rename, but we never write them again, and we do not delete them either, so a returning browser can be carrying all six theme entries at once. Clearing your site data removes them.
Next is the desktop-notification opt-in, and it does contain personal data: the key is sap.desktopNotifications.<your account id>, so your account identifier is written into your browser’s storage and stays there. It is scoped that way deliberately, so that on a shared browser a later sign-in does not inherit someone else’s opt-in, which also means a shared browser accumulates one such key per account that has ever enabled it there. Turning the notification off removes that account’s entry.
Another belongs to our product analytics (ph_…_posthog), and mirrors the cookie described above: a random device identifier and the state the SDK needs between pageviews. It is not your account id and it is not linked to one.
Separately from local storage, the analytics code uses your browser’s session storage. If you navigate away before the analytics library has finished loading, the events captured up to that point are parked under av_analytics_pending so they are not lost, up to 50 of them, each holding the event name, its sanitized properties and the time it happened. They are removed once sent, and session storage is discarded when you close the tab, so nothing here outlives the tab that wrote it.
All of them stay on your device except as described in the analytics paragraphs below, nothing else reads them back to us, and clearing your browser’s site data removes all of them. Your API keys are deliberately never written to browser storage of any kind.
What the analytics actually collects. Pageviews and a small number of product events. It is configured against its own defaults, deliberately: no session recording, no autocapture of clicks or form fields, no heatmaps, no scroll or performance properties, no error capture, no surveys or experiments, and no feature flags. URLs are reduced to a route template before they are sent, so a path carrying an identifier is reported as its shape rather than its contents, and query strings and fragments are dropped. It creates no person profile and we never associate the device identifier with your account. On the token-gated pages, the ones you reach through a token-bearing link, including the live card-code page, it never starts at all.
Because we load web fonts from Google Fonts, Google receives your IP address and user agent whenever a page loads. See §8.
14. Children
Agent Vault is not directed at children and is not for anyone under 18. We do not knowingly collect personal data from children. If you believe a child has given us personal data, contact legal@self.xyz and we will delete it.
15. Changes to this policy
We will update this page as the product changes, in particular as the gaps flagged in §5 and §10 are closed. When we make a material change we will update the effective date at the top and, for changes that affect how we handle data you have already given us, notify account holders by email before the change takes effect.
This document is in force as of the effective date shown at the top.
16. Contact
Privacy and data protection: legal@self.xyz
General support: legal@self.xyz
Legal notices: SocialConnect Labs, Inc. dba Self Labs, a Delaware corporation, at legal@self.xyz
See also the Terms of Service.