SecretInput¶
A secure input for sensitive values like API keys, passwords, and tokens. Values are never rendered in the DOM — they're masked with bullet characters and managed through a secure edit/reveal/copy workflow.
The input is read-only by default. Users click the edit (pencil) button to enter edit mode, and can optionally reveal the masked value or copy it to clipboard. Both show_toggle and show_copy are enabled by default.
Action buttons (left-to-right): Edit (pencil) enters edit mode, Copy (clipboard) copies value, Reveal (eye) toggles mask on/off.
Edit Mode¶
Clicking the edit button hides the masked input and replaces it with a resizable textarea — always empty (the current secret is never pre-filled). Confirm (✓) and cancel (✗) buttons appear overlaid at the top-right corner of the textarea.
- Confirm —
Ctrl+Enteror click ✓ — transmits the new value (base64-encoded) and restores the mask. - Cancel —
Escapeor click ✗ — discards the input and restores the previous mask.
Basic Usage¶
from pywry import SecretInput
api_key = SecretInput(
label="API Key",
event="auth:api_key",
placeholder="Enter API key",
)
Both show_toggle and show_copy default to True, so a basic SecretInput already has all three action buttons (edit, copy, reveal).
Disabling Action Buttons¶
# Only the edit button — no reveal or copy
SecretInput(
label="Password",
event="auth:password",
show_toggle=False, # Hide the reveal/eye button
show_copy=False, # Hide the copy button
)
With Custom Handler¶
For secrets stored in an external vault or environment variable:
import os
from pywry import SecretInput
def resolve_api_key(value, *, component_id, event, label=None, **metadata):
"""Custom handler for API key storage.
Parameters:
value: None to get the secret, str to set the secret
component_id: unique component ID
event: the event string
label: optional label text
"""
if value is None:
# Get mode - return the secret
return os.environ.get("MY_API_KEY", "")
# Set mode - store the secret
os.environ["MY_API_KEY"] = value
return value
SecretInput(
label="API Key",
event="config:api_key",
handler=resolve_api_key,
value_exists=bool(os.environ.get("MY_API_KEY")),
)
Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
label |
str |
"" |
Display label |
event |
str |
"toolbar:input" |
Event name emitted on interaction |
value |
SecretStr |
"" |
The secret value (never rendered in DOM) |
placeholder |
str |
"" |
Placeholder text shown when empty |
show_toggle |
bool |
True |
Show the eye reveal/hide button |
show_copy |
bool |
True |
Show the copy-to-clipboard button |
debounce |
int |
300 |
Debounce delay in ms for input events |
handler |
callable |
None |
Custom secret storage handler |
value_exists |
bool |
None |
Flag indicating a value exists externally |
component_id |
str |
auto | Unique ID for state tracking |
description |
str |
"" |
Tooltip/hover text |
disabled |
bool |
False |
Disable interaction |
style |
str |
"" |
Optional inline CSS |
Events¶
Emits the event name with payload on edit (when user exits the textarea):
value— the new secret value, base64-encodedencoded— alwaystrue(value must be decoded on the Python side)
Additional events:
{event}:copy— emitted when the copy button is clicked:{"componentId": "secret-abc123"}
The backend responds on{event}:copy-responsewith the decrypted value.{event}:reveal— emitted when the show/hide toggle is clicked:{"componentId": "secret-abc123"}
The backend responds on{event}:reveal-responsewith the decrypted value.
Common Patterns¶
API Configuration¶
config_toolbar = Toolbar(
position="top",
items=[
SecretInput(
label="API Key",
event="config:api_key",
placeholder="sk-...",
),
SecretInput(
label="API Secret",
event="config:api_secret",
placeholder="Enter secret",
),
Button(label="Save", event="config:save", variant="primary"),
],
)
Token Display¶
For displaying generated tokens:
import secrets
from pywry import PyWry, Toolbar, SecretInput, Button
app = PyWry()
def on_generate_token(data, event_type, label):
token = secrets.token_urlsafe(32)
app.emit("toolbar:set-value", {
"componentId": "token-display",
"value": token
}, label)
app.emit("pywry:alert", {"message": "New token generated!", "type": "success"}, label)
app.show(
"<h1>Token Generator</h1>",
toolbars=[
Toolbar(position="top", items=[
SecretInput(
component_id="token-display",
label="Token",
event="token:value",
),
Button(label="Generate New", event="token:generate"),
])
],
callbacks={"token:generate": on_generate_token},
)
Login Form¶
login_modal = Modal(
component_id="login-modal",
title="Login",
items=[
TextInput(label="Username", event="login:username"),
SecretInput(
label="Password",
event="login:password",
show_copy=False, # Don't need copy for password entry
),
Button(label="Login", event="login:submit", variant="primary"),
],
)
Security Notes¶
Client-Side Only
SecretInput masks display only. For true security:
- Never log secret values
- Use HTTPS in production
- Store secrets server-side when possible
- Consider environment variables for sensitive configs
API Reference¶
Bases: ToolbarItem
A password/secret input field with visibility toggle and copy button.
Displays a masked input by default with icons on the right side: - Eye icon to toggle visibility (requests secret from backend) - Copy icon to copy value to clipboard (requests secret from backend)
Icons appear on hover/focus and input text is padded to not overlap them.
SECURITY: The secret value is NEVER rendered in HTML. When the user clicks the show or copy buttons, JavaScript emits events to request the secret from the backend. The backend must handle these events and respond with the actual value. This ensures secrets are only transmitted on explicit user action and never embedded in the DOM.
Events emitted:
- {event} - On input change: {value, componentId}
- {event}:reveal - On show click: {componentId} - backend should respond with secret
- {event}:copy - On copy click: {componentId} - backend should respond with secret
Attributes:
| Name | Type | Description |
|---|---|---|
value |
SecretStr
|
Secret value stored as SecretStr (NEVER rendered in HTML). |
placeholder |
str
|
Placeholder text shown when empty (default: ""). |
debounce |
int
|
Milliseconds to debounce input events (default: 300). |
show_toggle |
bool
|
Show the visibility toggle button (default: True). |
show_copy |
bool
|
Show the copy to clipboard button (default: True). |
handler |
callable or None
|
Optional callable for custom secret storage.
Signature: |
value_exists |
bool or None
|
Flag indicating a value exists externally. If None, computed from value. |
Examples:
Simple usage with internal storage:
Custom handler with component metadata:
>>> def api_key_handler(
... value,
... *,
... component_id,
... event,
... label=None,
... **metadata,
... ):
... if value is None:
... return secrets_manager.get(component_id)
... secrets_manager.set(component_id, value)
... return value
>>> SecretInput(
... event="settings:api_key",
... handler=api_key_handler,
... )
Attributes¶
has_value
property
¶
Check if a secret value is set (without exposing it).
Returns True if: - value_exists is explicitly True, OR - value_exists is None and internal value is non-empty
Functions¶
build_html
¶
Build secret input HTML with visibility toggle and copy button.
When a value exists, a fixed mask (••••••••••••) is shown. Show/copy buttons emit events to request the real secret from the backend. Values are base64 encoded in transit for obfuscation.
Edit mode: - Click on edit button to enter edit mode - Switches to a resizable textarea (no wrapping, no formatting) - Confirm with blur or Ctrl+Enter - Cancel with Escape (restores mask) - Only transmits on confirm, not during typing
register
¶
Register this SecretInput in the secret registry.
Called automatically by Toolbar when building HTML. If a custom handler is provided, it's registered for reveal/copy events. Otherwise, the internal value is registered for retrieval.
update_secret
¶
Update the stored secret value.
Use this when receiving a new value from user input. If a custom handler is configured, it will be called to store the value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
new_value
|
str | SecretStr
|
The new secret value. If encoded=True and this is a string, it will be base64-decoded first. |
required |
encoded
|
bool
|
Whether the value is base64-encoded (for transit obfuscation). |
False
|
get_reveal_response_event
¶
Get the event type for reveal responses.
get_secret_value
¶
Get the current secret value.
If a custom handler is configured, calls handler with metadata. Otherwise, returns the internal value or from the registry.
Returns:
| Type | Description |
|---|---|
str | None
|
The secret value, or None if not set. |
auto_generate_component_id
¶
auto_generate_component_id() -> ToolbarItem
Auto-generate component_id based on type if not provided.
validate_event_name
classmethod
¶
Validate event follows namespace:event-name pattern.