Secrets
Passwords and keys that are typed into the app and seen by nothing else.
A secret is referenced by name and typed into the app by the CLI, at the moment of typing. The API, the model, the logs, the screens and the run record only ever see the name.
"goal": "Sign in with the email in data and the password in data",
"data": { "email": "alice@example.test", "password": "{{ secrets.staging_password }}" }Where values live
Your Keychain. testedok secret set staging_password asks for the value without echoing it and stores it in the macOS Keychain. testedok secret list shows names and dates, never values. testedok secret rm <name> removes one.
CI. Set TESTEDOK_SECRET_STAGING_PASSWORD (upper-cased, dashes to underscores). It wins over the Keychain.
The project, for the whole team. testedok secret set staging_password --org encrypts the value into the project's store; the dashboard's Secrets page does the same, and shows who set or rotated what, when. Owners and admins set them; any member's runs use them.
A run looks in the environment first, then the Keychain, then the project. A secret a flow needs that is set nowhere stops the run before the app launches, with a message naming all three ways to set it.
What never leaves your machine
- The decision model receives
{{ secrets.staging_password }}as the value to type, and the CLI substitutes the real value on the way to the keyboard. - After every action, the screen the API sees is masked: any known secret value becomes
••••••, and secure text fields lose their value entirely. The uploaded accessibility tree is masked the same way. - A screen that shows a secret gets no picture: no streamed frame, no screenshot. If a step on such a screen is escalated, the vision model gets the tree and a note instead of an image.
- Log lines are masked.
--videorecordings cannot be masked, so a run that showed a secret keeps its recording on your machine and does not upload it.
Values shorter than four characters are not masked (a PIN of "1" would blank every "1" on screen); those belong in secure fields, which are dropped regardless.
Shared secrets, technically
Values are encrypted with AES-256-GCM under a key per project, which is itself wrapped by a master key held only as a secret on the API. The database alone is ciphertext. Values are write-only for people: the only reader is a signed-in CLI about to type one, and it asks for the project that tests the app before the run is created. The dashboard can set values but is refused on every read.
Login profiles
Under an environment, a login profile lets a flow say "log in as alice":
environments:
staging:
logins:
alice: { user: alice@example.test, password: alice_pw } # alice_pw is a secret name
bob: { user: "+15550001111", method: code, code: "123456" }Mentioning alice in a goal adds alice_user and alice_password to the flow's data and a line of context saying how to use them; the password travels as {{ secrets.alice_pw }}. Methods: password, code (a static code the backend accepts, or a placeholder such as {{ inbox.code }}), magic-link.