Please do NOT open a public GitHub issue for security vulnerabilities.
Contact: development@securtel.net Expected response: within 72 hours Coordinated disclosure: 90 days before public disclosure requested
AlexClaw includes built-in session-based authentication. The web interface
is fully protected — all routes except /login require an authenticated session.
There is no anonymous access to any admin functionality.
Authentication is configured via the ADMIN_PASSWORD environment variable
(see .env.example). ADMIN_PASSWORD is always required — if it is not set,
the login page will show an error and no access is granted.
TOTP-based 2FA is available for sensitive Telegram commands.
Setup via /setup 2fa — compatible with any TOTP authenticator
(Google Authenticator, Authy, etc.).
Workflows can be marked Requires 2FA in the admin UI,
requiring code verification before execution.
AlexClaw only responds to messages from the configured TELEGRAM_CHAT_ID.
Messages from any other chat ID are silently ignored.
Do not share your bot token — anyone with the token can send commands
if they know or guess your chat ID.
The /webhooks/github endpoint verifies all incoming payloads using
HMAC-SHA256 with Plug.Crypto.secure_compare for timing-safe comparison.
Webhooks without a valid signature are rejected with 401.
If no webhook secret is configured, all webhooks are rejected.
Set github.webhook_secret in Admin > Config (GitHub category).
The /shell command allows the owner to run OS commands inside the container
for diagnostics (disk, memory, connectivity, BEAM status). It is protected
by 5 layers of defense-in-depth:
- Disabled by default —
shell.enabledmust be explicitly set totruein Admin > Config. The check is enforced both in the Dispatcher and inside the skill itself. - 2FA gate — every
/shellcommand requires TOTP verification when 2FA is enabled. - Whitelist with word-boundary check — the command must start with an allowed prefix (
df,free,ps,uptime,git, etc.). The prefix is boundary-checked:"df"allows"df -h"but not"define". The whitelist is stored as a JSON array in the database and editable from Admin > Config. - Blocklist — commands containing shell metacharacters (
&&,||,|,;,`,$(,>,<,\n) are rejected even if the prefix is whitelisted. - No shell interpretation — commands are executed via
System.cmd/3with arguments passed as a list (parsed byOptionParser.split/1). No shell is invoked — no globbing, no piping, no variable expansion.
Additional protections:
- Timeout — commands are killed after 30 seconds (configurable via
shell.timeout_seconds) - Output truncation — output is capped at 4000 characters (configurable via
shell.max_output_chars) - Workflow mode — when used in workflows, the command comes from step config (not user input), preventing injection through workflow chaining
The web-automator sidecar runs a real browser with network access. Automation recipes execute arbitrary browser actions — review recorded recipes before assigning them to scheduled workflows. The noVNC interface (port 6080) should never be exposed publicly.
Sensitive configuration values (API keys, tokens, OAuth secrets) are encrypted at the application level using AES-256-GCM before being stored in PostgreSQL.
- Encryption key is derived from
SECRET_KEY_BASEvia HKDF-SHA256 - Each value gets a unique 12-byte random IV — identical plaintext produces different ciphertext
- Encrypted values are stored with an
enc:prefix (base64-encoded IV + ciphertext + GCM tag) - Decryption happens transparently on boot (ETS cache holds plaintext for runtime use)
- The admin UI displays masked values — never raw ciphertext or full plaintext
Sensitive keys (automatically marked and encrypted):
telegram.bot_token, llm.gemini_api_key, llm.anthropic_api_key,
github.token, github.webhook_secret, google.oauth.client_secret,
google.oauth.refresh_token
Important: If you change SECRET_KEY_BASE, all encrypted settings become
unreadable. You will need to re-enter API keys and tokens via the admin UI
or environment variables and restart.
This feature is under heavy development. The sandboxing model is not yet hardened for adversarial inputs. Only load skills you trust.
Dynamic skills are compiled into the BEAM VM at runtime via Code.compile_file.
The following protections are in place:
- Path restriction — only files inside the configured
SKILLS_DIRvolume are accepted - Namespace enforcement — module must be
AlexClaw.Skills.Dynamic.* - Behaviour validation — module must export
run/1 - Permission sandbox — skills declare permissions;
SkillAPIenforces them at runtime. Undeclared permissions return{:error, :permission_denied} - Integrity checksums — SHA256 of source file stored on load, verified on boot. Mismatched files are skipped
- Core protection — core skills cannot be unloaded or overwritten by dynamic skills
- No NIF compilation — the Alpine runtime image has no build tools, preventing native code loading
Circuit breaker protection: Each skill (core and dynamic) is wrapped by an OTP circuit breaker. After 3 consecutive failures, the circuit opens and calls are rejected instantly without executing the skill. This prevents a failing dynamic skill from consuming resources or cascading failures through workflows. Workflow steps can be configured to skip or fallback to an alternative skill when a circuit is open or a skill is missing.
What is NOT sandboxed: A dynamic skill runs in the same BEAM VM as the rest of AlexClaw. A malicious skill could bypass SkillAPI by calling internal modules directly. The permission system is a guardrail, not a security boundary. Only load skills from sources you trust.
LLM prompts may contain user data. Workflow steps send data to external LLM providers (Anthropic, Google Gemini). Review which providers are enabled and their data retention policies before processing sensitive information.
Built-in login rate limiting. Failed login attempts are tracked per IP using ETS. After 5 failures (configurable), the IP is blocked for 15 minutes (configurable). Limits are adjustable at runtime from the Config UI without restart.
- Run behind a reverse proxy with TLS — never expose port 5001 directly
- Set a strong random
SECRET_KEY_BASE(mix phx.gen.secret) — this is also the encryption key material for sensitive config values - Set
ADMIN_PASSWORDto a strong random value - Restrict PostgreSQL to localhost or internal network only
- Built-in login rate limiting is active by default (configurable via Config UI)
- Never expose noVNC port (6080) publicly — it provides unauthenticated browser access
AlexClaw is designed as a single-user personal agent. Multi-user access control is not in scope. The authentication model assumes a single trusted operator.