mwguerra/web-terminal
Secure web terminal for Laravel with Filament integration. Run whitelisted commands locally or over SSH, with interactive PTY sessions, enum-based permissions, reusable scripts, and full audit logging for safe, traceable execution.
Architecture fit
--panel=, withoutTerminalPage(), only([])) and multi-tenant-aware (tenant_id on the logs migration, --with-tenant). Both match how we would actually deploy it: one privileged admin panel, tenant-scoped audit rows.Filament\Pages\Terminal, or embed WebTerminal::make()->local()->allowedCommands([...]) in any page. Configuration is declarative and lives in our codebase, not in a database UI — good for review and GitOps.TerminalPermission enum for page/action access, per-instance allowedCommands() for containment. Two layers is the right model; the question is whether the second layer is actually strong.Integration feasibility
@entangle → $wire.entangle in published views only). Manageable.Technical risk (ranked)
'git *', ls, pwd, cd, cat, grep) suggest prefix/glob matching on a command string. Pattern-matching allowlists are a well-known weak primitive: argument injection (git -c core.pager='sh -c ...', --upload-pack, -o on tar/find), shell metacharacters if the string is passed to sh -c, and cat on arbitrary paths (cat .env, cat /proc/self/environ). This must be verified before we treat the whitelist as containment. Until proven otherwise, assume it is a guardrail, not a sandbox.artisan tinker, queue:work, reverb:start implies sessions that outlive a normal request. Concurrency, worker occupation, and orphaned-session cleanup are real operational questions, not edge cases.Key questions (to resolve before a go decision)
argv[0] only, or exact command equality? Are user-supplied arguments passed as an argv array (no shell) or interpolated into a shell string?Crypt, or stored plaintext in config/env? Are they ever written to the audit log?Stack fit
Migration path
composer path/VCS repo) so we can patch the containment logic ourselves if a security issue sits unfixed. Single maintainer + real shell access makes this a reasonable precondition, not paranoia.Compatibility
"^2.0" with a committed lockfile) and add a CI job that fails on version drift outside the tested matrix. The Filament 5 / Livewire 4 pairing is new enough that minor bumps can break Blade entanglements and component lifecycle assumptions.--panel= and disable plugin defaults with withoutTerminalPage() / withoutTerminalLogs() — otherwise we get duplicate navigation entries, which is a confusing but low-severity failure.Sequencing
vendor/, and empirically test 8–10 allowlist bypass candidates against a ls/cat/git * config. Output is a written verdict: guardrail or sandbox.canAccess() policy; named operator role; OS-level controls (unprivileged user, read-only FS, no prod secrets in the terminal's env); explicit exclusion list for readers (env, cat, printenv).mwguerra/web-terminal-stream ships and passes the same spike.Maintenance
composer update.systemctl *") is a security review, not a config tweak. That recurring cost is the thing most likely to erode the design over 12 months.Support
Scaling
queue:work, reverb:start, tinker) mean workers are occupied by humans, not requests. Cap concurrent terminals deliberately, or a handful of operators can starve the panel.tail -f or a verbose queue:work session can write megabytes per session. Retention policy and output truncation are required, not optional.Failure modes
env, cat .env, cat /proc/self/environ) if the allowlist is loose.Ramp-up
How can I help you explore Laravel packages today?