Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Web Terminal Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • Ships as a conventional Laravel package: service provider + publishable config + publishable migration + Filament plugin + Livewire component + Filament Schema component. This is the lowest-friction shape for our stack and requires no new runtime (no sidecar, no Node service, no queue topology) for Classic mode.
  • Multi-panel-aware (--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.
  • The extension surface is sane: subclass 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.
  • AuthZ is split cleanly: 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

  • Hard version gate. v2.x requires Laravel 12–13 / Filament 5.x / Livewire 4.x / PHP 8.3+. v1.x is the only option on Laravel 11 / Filament 4 / Livewire 3. This is a blocking pre-check — determine which side of that fork we are on before anything else, because it changes the package version, the API surface, and the support horizon.
  • Installation is a single interactive or fully non-interactive artisan command with flags for config/migration/views/page/resource/migrate. That is unusually CI-friendly; we can codify the install in a provisioning step rather than a wiki page.
  • Upgrade friction from v1→v2 is documented and small (@entangle → $wire.entangle in published views only). Manageable.
  • Main integration cost is not code — it is security review and access-control design. Budget for that, not for wiring.

Technical risk (ranked)

  1. Command whitelisting is the entire security boundary, and it looks string/pattern-based. Examples in the docs ('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.
  2. Blast radius is the app server itself. Commands run as the PHP process user. If that container holds DB credentials, API keys, or cloud role tokens in env/files, an allowlist that includes a generic reader is equivalent to credential exfiltration. Needs OS-level defense in depth (dedicated unprivileged user, read-only FS, egress restrictions, no shared secret material) — the package cannot provide this.
  3. Audit logging is detective, not preventive. Full command + output rows in our DB are a valuable trail, but the trail is writable by anyone with DB access and is stored on the same host being operated on. Ship to an append-only/offsite sink if it must survive a compromised host.
  4. Stream mode is frozen and slated for removal in a major. The README's own framing ("without exposing a raw terminal") conflicts with Stream mode's full PTY with vim/htop. Do not build against it. Treat it as out of scope and plan for the eventual major-version removal.
  5. Maturity signal is thin. 30 stars, 0 dependents, single maintainer, MIT. Readme + docs + changelog are present and unusually candid (the split rationale is a good sign of engineering discipline), but there is no downstream battle-testing and no ecosystem pressure on security. Score 1.01 reflects that.
  6. Long-running work in a web request context. Interactive PTY/tmux for 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)

  • How exactly is the allowlist matched — full-string regex, argv[0] only, or exact command equality? Are user-supplied arguments passed as an argv array (no shell) or interpolated into a shell string?
  • Is there any per-argument validation or path restriction, or is the operator free-form after the allowed prefix?
  • Are SSH credentials encrypted at rest via Crypt, or stored plaintext in config/env? Are they ever written to the audit log?
  • What enforces rate limiting and concurrency, and what happens to a running command when the Livewire component unmounts or the request times out?
  • How is tenant isolation enforced on the logs — a global scope, or just a column we must filter ourselves?
  • Is there a security disclosure policy, and what is the actual test coverage on the containment path?
  • What is the maintainer's stated support window for 1.x and 2.x, and will a security fix be backported to 1.x?
  • Does the migration's log table have retention/rotation, or is output stored indefinitely?

Integration Approach

Stack fit

  • Only viable on Laravel 12+/Filament 5/Livewire 4/PHP 8.3+ at v2. If our admin panel is on Filament 4/Livewire 3, we either take v1 (and inherit a version that is now on a legacy track) or defer until the panel upgrades. Decide this first; it is the single biggest determinant of feasibility.
  • Requires Filament. Without a Filament panel there is no page/resource — we'd be embedding the Livewire component manually and losing the logs UI and widgets, which materially reduces the value proposition.
  • No new infrastructure for Classic mode. That is the strongest argument for it over standing up a separate bastion or a Teleport-style gateway.

Migration path

  • Greenfield capability — nothing to migrate in, no data backfill. The only migration is the package's own logs table, which is additive.
  • For the Stream-mode removal later: nothing to migrate if we never adopt Stream. Explicitly write that into the decision record so no one enables it "just to try" and then depends on it.
  • Contingency: vendor the package (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

  • Pin exactly ("^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.
  • Multi-panel apps: generate pages per panel with --panel= and disable plugin defaults with withoutTerminalPage() / withoutTerminalLogs() — otherwise we get duplicate navigation entries, which is a confusing but low-severity failure.
  • Localization is EN + PT-BR only. Fine if our operator base is English-speaking; a small translation task otherwise.

Sequencing

  1. Version fork check — confirm Laravel/Filament/Livewire/PHP versions against the matrix. Stop here if misaligned.
  2. Security spike (timeboxed, non-prod) — install into an isolated environment, read the command-execution path in vendor/, and empirically test 8–10 allowlist bypass candidates against a ls/cat/git * config. Output is a written verdict: guardrail or sandbox.
  3. Containment design — dedicated Filament panel or strict 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).
  4. Logging pipeline — enable the audit trail, confirm what fields are captured, add retention, and forward to the central log sink. Do this before the first pilot user, not after.
  5. Pilot — local connection only, one non-production host, two engineers, restrictive allowlist, two-week observation window.
  6. SSH expansion — only after pilot review; each SSH target gets its own allowlist and its own credential-rotation story.
  7. Stream mode — explicitly out of scope. Revisit only if mwguerra/web-terminal-stream ships and passes the same spike.

Operational Impact

Maintenance

  • Bus factor 1. Small, low-traffic repo with no dependents means security issues may be discovered by us, not upstream, and fixed on the maintainer's schedule. Mitigations: vendored fork, pinned versions, and a documented patch procedure.
  • The package's roadmap already includes a breaking major (Stream removal). Small, but it means an upgrade will eventually require a deliberate version decision rather than routine composer update.
  • Our own maintenance load is the allowlist. Every new operator request ("just add 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

  • Community-only: GitHub issues, no SLA, no commercial backing. Acceptable for an internal ops convenience; not acceptable for anything customer-facing or on a compliance-critical path.
  • Internally we own the runbook: who gets access, how allowlists are reviewed, what to do when a command is run against the wrong host, how to prove what happened. Documented changelog + README quality helps here — the docs are genuinely better than the star count suggests.

Scaling

  • Vertical only, effectively. Each terminal session pins a PHP worker for the duration; long-running interactive sessions (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.
  • Multi-node deployments need sticky sessions for any stateful session; Classic mode is comparatively stateless, Stream mode (frozen) is not.
  • Audit log growth is the sneaky one: full command output in the DB means tail -f or a verbose queue:work session can write megabytes per session. Retention policy and output truncation are required, not optional.
  • Operator count doesn't scale linearly with value — this is a tool for a small number of trusted engineers. Design for 5 users, not 50.

Failure modes

  • Allowlist bypass → RCE on the app host. Highest severity, lowest detectability without offsite logs.
  • Credential exposure via generic readers (env, cat .env, cat /proc/self/environ) if the allowlist is loose.
  • Privilege creep — the page is enabled, then shared, then granted to a broader role, and suddenly non-technical staff have shell access. The package's own warning exists precisely because this happens.
  • Audit log loss or tampering — DB restored from backup, rows pruned, or host compromised. Detective control fails silently.
  • Orphaned PTY/tmux sessions consuming workers or holding file locks after a browser closes or a deploy restarts the app.
  • Livewire request timeout on a command that outlives the request — the operator sees a failure while the command actually succeeded. Dangerous for anything mutating.
  • Version drift on Filament 5/Livewire 4 producing broken pages rather than clear errors.
  • Duplicate nav entries from custom page + plugin defaults both enabled — cosmetic, but it's the kind of thing that signals the install wasn't reviewed.

Ramp-up

  • Developer ramp-up is genuinely low: it's a Filament page, install is one command, and the embeddable component is a few lines. An engineer is productive in under an hour.
  • Operational ramp-up is high and is the real cost. Introducing a shell path into a web app requires an access-review process, an allowlist review cadence, an incident procedure for "wrong command on wrong host," log retention policy, and a security sign-off. That work dwarfs the integration.
  • Recommend positioning this as an internal break-glass / ops convenience tool for a named, small operator group — with OS-level containment doing the real work and the package's whitelist treated as a guardrail. If the requirement is actually "give non-engineers safe operational controls," this package is the wrong tool; scoped, purpose-built actions are.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin