Omahub
← All plugins
A

Forge

by acobrerosf

Laravel Forge server health and deployment status in the Omarchy bar, across every organization you have a token for, with one-key deploys.

Security review

No obvious issues detected

Deterministic scan — not a security guarantee

None
Risk level
None
Analyzed commit
730661d
Scanned
2 weeks ago

No potentially dangerous behavior detected in the analyzed commit.

Automated analysis only — not a security guarantee.

AI advisory review

No obvious issues detected

Language-model assessment · ~deepseek/deepseek-v4-flash-latest — advisory only

None
AI risk level
None
Recommendation
install
Model
~deepseek/deepseek-v4-flash-latest
Analyzed commit
730661d
Reviewed
2 weeks ago

This plugin is carefully engineered with strong security practices: API tokens are stored in the system keyring and only accessed by a bash helper, the QML never handles credentials, destructive actions require explicit two-key confirmation, and rate-limit budgets keep the Widget from abusing the Forge API. The deterministic scan found nothing, and manual review of the sampled code confirms no hidden persistence, credential theft, or destructive behavior beyond the user-configured, explicit operations.

How this check works

This review combines the deterministic scan (the rule-based results above) with an independent look at the plugin's code by a language model. The model reads a trimmed sample of the repository's files, the manifest, and the README, then gives a plain-language risk level and a recommendation: install (no notable danger), review (look closer first), or avoid (clearly dangerous).

It runs on the same analyzed commit as the deterministic scan and is strictly advisory — it is not a security guarantee and never blocks a plugin by itself. A human moderator still reviews plugins before they are listed.

AI advisory only — automated analysis, not a security guarantee.

Install
$ omarchy plugin add https://github.com/acobrerosf/omarchy-forge --enable
Developer Tools #quickshell

Forge

Laravel Forge servers and deployments in the Omarchy bar. The icon tells you whether anything is broken; the panel tells you what, and lets you fix it without opening a browser.

Uses the Forge v2 API. One icon covers every organization you have a token for.

<img src="screenshots/panel.png" alt="The panel, with two organizations and a server unfolded into its sites" width="420">

Getting started

1. Install the plugin.

omarchy plugin add https://github.com/acobrerosf/omarchy-forge.git --enable

2. Create a Forge API token at https://forge.laravel.com/profile/api.

Tick these scopes:

Scope What it buys you
user:view required — confirms the token works
organization:view required — finds the organizations to watch
server:view required — servers, sites, deployment status
site:manage-deploys deploy from the bar, and read deployment logs
server:manage-logs read a site's application and nginx logs
server:manage-services restart nginx, PHP-FPM, supervisor, redis or the database; reboot a server
site:manage-commands run a command on a site, toggle maintenance mode
recipe:view list the organization's recipes and follow a run
recipe:manage run a recipe on a server

The first three are the minimum. Leave the rest off and the widget is read-only — everything that would change something says which scope it wanted instead of failing quietly. The rest are named for what they let you change, with two exceptions in each direction: deployment logs and site logs are reads that Forge files under a write scope anyway, so a strictly read-only token is refused those two and told which scope it wanted; and recipe:view is a genuine read scope, wanted for the recipe list and for following a run that recipe:manage started.

3. Add the token. Click Set up Forge in the panel, or run:

~/.config/omarchy/plugins/acobrerosf.forge/omarchy-forge setup

It asks for the token, then lets you pick which of that token's organizations to watch.

4. That's it. The Forge mark appears in the bar. Click it, or press its key, and the panel opens with your organizations. Move with j/k, press enter to unfold, press d on a site and then Y to deploy it.

Tokens are stored in your login keyring, never in a config file, and the widget never handles them — every request is made by the bundled omarchy-forge helper.

Reading the bar

<img src="screenshots/bar.png" alt="The Forge mark in the Omarchy bar" width="224">

The mark carries one badge for everything being watched, showing the worst thing it can see:

Badge Meaning
none everything is up and no deployment is failing
hollow a site is in maintenance mode
pulsing a deployment is running
warning no token, a missing token, or the last request errored
solid a server is unreachable, or a deployment failed

Middle-click the icon to refresh.

Using the panel

Organizations unfold into servers, servers into sites with their latest deployment, branch and commit. A server whose site starts failing unfolds itself once.

Key Action
j k or ↑ ↓ move the cursor
enter / space unfold an organization or server, or open a site
l or → go deeper — on an unfolded server, opens its actions
h or ← back, or fold up the current row
d arm a deploy of the site under the cursor — Y sends it
e the server's event feed — what Forge has done to it, newest first
o open the site's URL, or a server's page in Forge
f open the row in the Forge dashboard
s copy an ssh forge@… command for the server
r refresh now
a add an organization
Y send whatever enter, a click or d has armed
esc back one level, or close

Anything that changes something takes two keys: enter (or d, or a click) arms the row and says so, and a capital Y sends it. Any other key cancels — enter again, a lower-case y, j, escape — and does nothing else; an arm left alone lapses after eight seconds. Y rather than a second enter, so a doubled keypress or a mistyped j or k can never be the last key before something changes on a server.

The line at the bottom of the panel names what the row under the cursor can do.

Mouse: left click a row to unfold it or open a site; click the ⚙ on a server row for its actions; right click to open it in Forge. Inside a view, the trail at the top left is the way back. A click arms an action but never sends one — that is always Y — and a click while something is armed cancels it.

<img src="screenshots/deploy.png" alt="A site row armed, reading 'press Y to deploy'" width="420">

A site

Opening a site gives you its actions and its details, all from data the refresh already fetched.

Actions deploy, toggle maintenance mode, reload its PHP-FPM, run a command, read the deployment log, read the three site logs, list its heartbeats, open the site, open it in Forge, copy its ssh command
Details PHP version, app type, status, maintenance mode, isolation, zero-downtime, releases kept, aliases, healthcheck

An action that can't run says why — a site with no repository, or one that has never deployed.

Maintenance mode takes the site offline behind a 503, and brings it back the same way. Forge does the work out on the server, so the row says enabling… until it has landed.

Reload PHP-FPM gracefully reloads the pool for the site's own PHP version. The server view's PHP rows act on the server's default version, which is the wrong pool for an isolated site running another one.

Run a command opens a prompt: type a command, enter to arm, Y to run. It runs as forge in the site's directory. Nothing is remembered between opens — no history, no repeat key, on purpose. The output arrives when the run finishes.

Deployment log opens the latest deploy's output, ANSI stripped, scrolled to the bottom.

Application log, Nginx error log and Nginx access log open what the site itself is writing, in the same pane. Forge hands back the tail of each rather than the whole file, as of the moment you asked — r asks again. The nginx error log is the one to open when a site is up and answering 500. An empty log says so rather than showing Forge's placeholder line.

Site logs need the server:manage-logs scope. That is Forge's name for it and it reads like a write scope, because the same scope clears them; a token without it is told so on the pane.

Heartbeats

A heartbeat is a dead-man switch: something out there — a cron job, a queue worker — is supposed to ping Forge on a schedule, and Forge raises the alarm when it stops. Heartbeats lists the ones this site has, worst first, with what each is called, whether it is beating, missing or still pending, how often it is expected and how much grace it gets.

Read when you ask and never polled, so r looks again. Forge records no timestamps on a heartbeat, so a row cannot say when one was last seen — its schedule is what makes a missing legible. The ping URL is deliberately not shown: it is the credential that marks the site alive, and it belongs in the dashboard rather than in a bar widget. Creating and editing heartbeats is the dashboard's too; this reads them.

Key In a log, or a command's, a recipe's or an event's output
j k or ↑ ↓ scroll
g / G top / bottom
c copy the whole thing
w save it to ~/Downloads/ — the panel says where it landed
r look at a site log, a command or recipe run, or an event's output again
h or esc back

A server

Press l on an unfolded server, or click the ⚙ on any server row.

Restart nginx the web server in front of every site on it
Reload PHP-FPM a graceful reload of the server's PHP version
Restart PHP-FPM the harder version
Restart supervisor every queue worker and daemon on the server
Restart redis the server's redis, and whatever its sites keep in it
Restart MySQL or MariaDB, or Postgres — named for what the server runs
Reboot server every site on it goes down with it; still offered when the server is unreachable
Server events what Forge has done to the server, newest first — also e from any row
Run a recipe the organization's saved scripts; enter arms one, Y runs it here
Monitors Forge's own alerts for this server — CPU, disk, memory — and which are firing

A server lists only the services its type runs, since Forge reports no service status to go on: an app server has all of them, a web server no redis or database, a worker PHP and supervisor, a load balancer nginx alone, a database or cache server its one service. A server with no database has no database row, and a type the widget doesn't know lists everything and lets Forge answer.

Forge does all of this asynchronously, so the answer is "requested" — what changed shows up in the next refresh.

Events

Every deploy, command run, key install and environment change Forge performs on a server is an event, and the feed lists them thirty at a time with the site each was about and when. Enter on one opens what it printed, in the same pane as a deployment log and with the same keys; enter on the last row, Older events…, fetches the next thirty. An unreachable server's feed is where the reason usually is.

Forge records what an event did and what it printed, not whether it succeeded — there is no status on an event, so the feed has no red rows. Open the output to find out. Reading events needs only the server:view scope, so unlike deployment logs this works on a read-only token.

Recipes

A recipe is a shell script saved in your Forge organization. Run a recipe lists the ones this organization has; enter arms one and Y runs it, on the server whose view you opened the list from — one server at a time, which is the difference between this and the dashboard. The output arrives when the run finishes, in the same pane as a deployment log and with the same keys.

The row says who the recipe runs as, root or forge, and its first line. It will not arm on a server that is not ready, and says which instead. Nothing here can edit or create a recipe: this runs what is already written, and the dashboard is where writing it belongs.

Monitors

Forge can watch a server's CPU load, disk and memory and alert on a threshold. Monitors lists what this server is watching — CPU load ≥ 90%, Disk ≥ 80% — with how often Forge checks it, who gets the email, and whether it is firing. A firing monitor is red and says how long it has been that way; one Forge is still installing, or failed to install, says that instead.

Read when you ask and never polled, so r looks again. Reading them needs only server:view, the scope the refresh already uses. Creating and deleting monitors stays in the dashboard, where the threshold can be thought about.

Forge answers the run with no id to follow, so the widget recognises the run afterwards in the recipe's own list of runs — by the server, and by being newer than the last one it followed. That list has no sort and pages 30 at a time, so a recipe with a long history is searched three pages deep before a look gives up and tries again.

Notifications

Deployments that finish or fail between refreshes raise a desktop notification, naming the organization when more than one is watched. Clicking it opens the site.

More than one organization

One token belongs to an account; an account can see several organizations. A token that already sees three organizations only needs adding once. An organization on somebody else's Forge account needs a token of its own.

Press a in the panel, or:

omarchy-forge add                          # store a token, pick its organizations
omarchy-forge accounts                     # who is configured, and what each one watches
omarchy-forge login --account clientco     # replace a token after rotating it
omarchy-forge remove acme                  # stop watching one organization
omarchy-forge remove --account clientco    # drop an account, its token, and its orgs

All of them show in one list, grouped per organization. Nothing needs restarting — a new organization is picked up within a refresh.

Settings

Editable in Setup → Plugins, or in ~/.config/omarchy/shell.json:

Key Default What
refreshIntervalSec 60 seconds between refreshes (15–3600)
watchDeployments true also fetch sites and deployment status
notifyDeployments true notify when a deployment finishes or fails
organization "" which organizations this copy shows — empty for all
dashboardUrlTemplate https://forge.laravel.com/{org}/{server}/{site} where f and right-click point — {org}/{server} are slugs, {site} an id, {serverId} also available

organization is a filter, not a second place to configure one. A slug narrows the widget to that organization; a comma-separated list narrows it to those. You can put a second copy in the bar pinned to one organization if you would rather have separate icons.

Rate limits

Forge allows 60 requests a minute per Forge account, shared with anything else on it — including its own dashboard in a browser tab.

A refresh costs two requests per organization, whatever your server count, and that figure doesn't change with the number of monitors. Opening a deployment log is one more, and so is a site log, and so is a server's event feed — one per page of thirty, and one for each event's output you open, one per page of the recipe list, one per page of a server's monitors and one per page of a site's heartbeats. Running a command costs about a dozen, spread over a hundred seconds, and running a recipe costs the same.

The widget keeps a budget per account and backs off before it runs out, telling you on the row rather than failing silently. If Forge refuses anyway, the panel says "rate limited" and everything on that account waits until the limit resets, then resumes on its own.

Organizations with more than 150 sites are checked in rotation across several refreshes — nothing is dropped, but a status change at the far end can be noticed one rotation late. The panel says when that is happening.

The CLI

omarchy-forge works on its own, and is useful for scripting even if you never open the panel:

omarchy-forge setup           # guided setup
omarchy-forge add             # store a token and pick organizations to watch
omarchy-forge accounts        # accounts, tokens, and what each one watches
omarchy-forge orgs [account]  # organizations a token can see
omarchy-forge org [slug]      # show or set the default organization
omarchy-forge remove <slug>   # stop watching an organization
omarchy-forge rename OLD NEW  # give an account a different CLI handle
omarchy-forge logout [account]             # remove an account's token
omarchy-forge status [--org SLUG]          # server health as a table
omarchy-forge doctor                       # every account: token, auth, rate limit left
omarchy-forge api --account default GET /orgs/acme/servers      # raw request

remove and logout say what they are about to drop and wait for a capital Y, the same as the panel. --yes answers for them, and a script needs it: with no terminal to ask on they refuse rather than assume.

To have it on your PATH:

ln -sf ~/.config/omarchy/plugins/acobrerosf.forge/omarchy-forge ~/.local/bin/omarchy-forge

api always prints one JSON envelope, whatever went wrong:

{"ok": true, "status": 200, "rateRemaining": 57, "rateReset": null, "body": { }, "error": null}

A third argument is a JSON request body; pass - to read it from stdin instead. --account names the credential to use and --org picks it by organization; with neither, the default organization's account is used. $FORGE_TOKEN (with $FORGE_ACCOUNT) overrides the keyring for one run.

State lives in ~/.local/state/omarchy/forge.json — which accounts exist, which organizations each reaches, and which is the default. Tokens are never in there.

Requirements

  • Omarchy 4.0 or newer
  • curl, jq, secret-tool — all on a stock Omarchy install
  • wl-copy for the copy actions
  • gum, optionally — nicer organization picking; there is a numbered fallback without it

Known limits

  • The dashboard URL is a template. The Forge API hands out no web link, so dashboardUrlTemplate is a setting.
  • The server view restarts, it never stops. Stopping a service and power-cycling a server are deliberately absent: a service stopped from the bar is one nothing here could start again.
  • Events have no status. Forge's event record says what was done and what it printed, not whether it worked, so the feed cannot colour a failed step — open its output to find out.
  • The server list stops at 150 rows and says so rather than quietly showing a prefix. Sites are not capped — past 150 they are checked in rotation.
  • A command run has no history and no partial output. You see the run you just started, and its output arrives when it finishes. A recipe run is the same, and adds one of its own: it goes to one server at a time, where the dashboard can send a recipe to several at once.
  • A recipe run is recognised, not received. Forge's answer to starting one carries no id, so the run is found afterwards in the recipe's run list — three pages of thirty deep. A recipe with more than ninety runs on record can outrun that, and the pane says it is still looking.
  • Monitors and heartbeats are read, never watched. Both are on-demand views, so a monitor firing or a heartbeat going missing never changes the bar icon and never raises a notification. Forge offers no organization-wide list of either and no way to attach them to the server or site requests the refresh already makes, so watching them would cost one request per server plus one per site on every tick — a second sweep, against a budget the flat two-per-organization figure above depends on.
  • Logs need a scope named for writing. Forge gates deployment output behind site:manage-deploys and a site's own logs behind server:manage-logs, the scope that clears them. That is Forge's choice, not this plugin's — a strictly read-only token can watch a deployment fail and not be told why.

License

MIT