Security

AI Agent Permissions for Server Management: How Panelica's Scoped API Keys, HMAC Signing and Rate Limits Keep an Assistant Safe

Back to Blog
Managing servers the hard way? Panelica gives you isolated hosting, built-in Docker and AI-assisted management.
Start free

Panelica keeps an AI agent safe on a production server the same way it keeps any API consumer safe: scoped API keys with nothing preselected, HMAC-SHA256 request signing with a secret that never leaves the client machine, IP whitelisting and expiry, tiered rate limits, and a risk label on every one of the 404 available tools so a client can ask before anything destructive runs.

50
scoped permissions, none preselected
46
tools explicitly labeled destructive
HMAC-SHA256
algorithm signing every request
±5 min
timestamp window before a signed request is rejected

What Is the Threat Model of Giving an AI Agent Server Access?

An AI assistant with API access to a hosting panel introduces a specific, nameable set of risks — not a vague new category of danger, but four familiar problems that any credential-issuing system has to answer.

Over-broad tokens

A single all-powerful token handed to an assistant means every mistake, every misunderstood instruction and every unexpected model behavior carries the full blast radius of root access. The fix is not trusting the model more; it is never handing out more authority than the task in front of it requires.

Destructive actions taken without a human noticing

Deleting a database, removing a backup or suspending an account are all legitimate operations — the danger is a client executing one of them as a side effect of a broader instruction, without anyone realizing it happened until later. The fix is making the danger visible at the protocol level, before the call executes, not after.

Prompt injection through content the assistant reads

An assistant that reads log files, DNS TXT records, file contents or webhook payloads as part of its work is reading attacker-controlled text sometimes. If that text can talk the model into calling a tool it otherwise would not, the credential behind the session is what limits the damage — not the model's judgment, which an injected instruction is specifically trying to defeat.

Runaway loops and rate exhaustion

An assistant that retries aggressively, or fans out many calls while investigating something, can behave like a low-grade denial-of-service against its own panel, or simply burn through an API budget faster than intended. The fix is a hard ceiling the assistant cannot negotiate its way past.

Panelica answers all four with the same layer: the scoped, signed API key described below, plus a set of instructions the assistant itself is told to follow before it treats a tool as safe to call.

How Do I Create a Properly Scoped Key?

1
Open Settings > API Keys > Generate API Key in the panel, signed in as an account permitted to manage keys.
2
Search the 50 available scopes and select only the ones this session needs — nothing is preselected, and the search understands family names like "domains" or "backups."
3
Optionally set an IP whitelist and an expiry (expires_in days) so the key stops working on its own once the task is done.
4
Copy the key and secret. The secret is shown once; if it is lost, revoke the key and generate a new one rather than trying to recover it.

How Do Scoped API Keys Limit What an Assistant Can Do?

When you generate an API key in the panel for an AI assistant to use, it starts with zero permissions. You search across 50 distinct scopes and add only what the task in front of you actually requires — nothing is granted by default, and nothing is preselected in the create-key dialog.

AreaScopes
Accountsaccounts:read, accounts:write, accounts:delete
Domains & subdomainsdomains:read, domains:write, domains:delete
Databasesdatabases:read, databases:write, databases:delete
DNSdns:read, dns:write, dns:delete
Emailemail:read, email:write, email:delete
FTPftp:read, ftp:write, ftp:delete
SSLssl:read, ssl:write
Backups & snapshotsbackups:read, backups:write, backups:restore
File Managerfiles:read, files:write, files:delete
Cloudflarecloudflare:read, cloudflare:write, cloudflare:delete
Docker & app templatesdocker:read, docker:write, docker:delete
App hosting (Laravel / Node.js / Python)apps:read, apps:write, apps:delete
Git & deploygit:read, git:write, git:delete
Logs & auditlogs:read, logs:write
Security (antivirus, firewall, IP blocks)security:read, security:write, security:delete
Server & infrastructureserver:read, server:write
Service controlservices:restart, services:start, services:stop
Plansplans:read, plans:write
Webhookswebhooks:read, webhooks:write, webhooks:delete
Bandwidthbandwidth:read
Licenselicense:read
Migrations (panel-to-panel)migrations:read
Terminalterminal:access
Full access*:*

Every family also accepts its own wildcard — domains:* grants read, write and delete on domains in one grant — and action-level wildcards work across families too: *:read grants read access everywhere, *:write everywhere a write scope exists, *:delete everywhere a delete scope exists, and *:* grants everything. The grid below shows a representative slice of that catalogue as a read/write/delete matrix.

read
write
delete
Domains
domains:read
domains:write
domains:delete
DNS
dns:read
dns:write
dns:delete
Databases
databases:read
databases:write
databases:delete
Backups
backups:read
backups:write
backups:restore
SSL
ssl:read
ssl:write
Server
server:read
server:write
Services
services:start / restart
services:stop

A representative slice of the 50-scope catalogue — each family accepts its own read/write/delete grant independently, plus a family wildcard and the global *:* grant not shown here. The full 24-family table above is exhaustive.

A key intended purely to read metrics and restart a stuck service needs server:read plus services:restart; it does not need delete access to backups or write access to DNS, and nothing forces you to grant either. Service control deliberately keeps its own action scopes separate from server:write for exactly this reason — a metrics-only server:read key cannot stop MySQL.

*:* grants everything — every read, every write, every delete, across every family, with no further check. Reserve it for a genuinely trusted, well-understood automation running from a controlled environment, not as a default shortcut to avoid picking scopes.

How Do Roles Interact With Scopes?

Scopes sit on top of Panelica's existing role hierarchy, they do not replace it. An API key inherits the role of the account that created it — ROOT, ADMIN, RESELLER or USER — and every call the key makes is still filtered through that role's normal visibility rules. A key created under a USER account only ever sees that account's own domains, databases and files, no matter how broad its scopes are; a scope like domains:read on a USER key cannot see another customer's domains, because the role boundary applies first. Some routes are restricted further still, to ROOT or ADMIN accounts specifically — access logs are one example — regardless of what scopes a lower-role key holds. An assistant connected through a RESELLER-owned key is boxed in by that reseller's own customer set before scopes are even evaluated.

How Does HMAC-SHA256 Request Signing Work?

Every request the MCP server — or any other External API client — sends to the panel is signed over METHOD + PATH + QUERY + TIMESTAMP + BODY using the API secret, producing a signature sent as the X-Signature header alongside X-API-Key and X-Timestamp. The panel recomputes that signature server-side and rejects the request if it does not match, and separately rejects any request whose timestamp has drifted more than five minutes from the panel's own clock — which closes the door on replaying a captured request later, since a signature captured today is useless against tomorrow's timestamp check.

# Signature is over METHOD + PATH + TIMESTAMP + BODY
$ TS=$(date +%s)
$ SIG=$(printf "GET/v1/api-keys${TS}" | openssl dgst -sha256 -hmac "$PANELICA_API_SECRET" -hex | awk '{print $2}')
$ curl -sk "$PANELICA_BASE_URL/v1/api-keys" -H "X-API-Key: $PANELICA_API_KEY" -H "X-Timestamp: $TS" -H "X-Signature: $SIG"
{"status":"success","data":{"api_keys":[...]}}
# a valid signature and a timestamp inside the 5-minute window is the entire trust boundary

The part that matters most for an AI workflow specifically: the secret itself never travels. It lives in the environment of whatever machine is running the MCP client — your laptop, a CI runner, a container — and is used locally only to compute the signature. It is never logged, never sent to any third party, and never written to disk by the MCP server. If that machine is compromised, the signing key is exposed the same way any local secret would be; but nothing about the AI layer adds a new place for the secret to leak, because it never leaves the one place it started.

How Do IP Whitelisting, Expiry and Instant Revocation Add Defense in Depth?

Scoping controls what a key can do; these three controls limit where, how long, and until when. A key can be restricted to a specific IP address or range at creation time, so a stolen key is useless from anywhere else. A key can be given an expiry in days at creation time (expires_in), so a temporary integration — a one-off migration script, a contractor's session — stops working on its own instead of relying on someone remembering to revoke it. And any key, at any time, can be revoked instantly from the panel: the very next request using it fails immediately, with no grace period and no cached authorization lingering on the panel side.

What Do Rate Limits Protect Against?

Rate limits exist specifically for the runaway-loop and resource-exhaustion case: an assistant that fans out many calls while investigating a problem, or retries aggressively after a transient failure, hits a hard ceiling instead of degrading the panel for everyone else using it. Every key carries a tier set at creation time:

TierRequests per minute
Starter60
Professional300
Business1,000
EnterpriseUnlimited

Responses carry X-RateLimit-Remaining-Minute and X-RateLimit-Reset-Minute headers, and a 429 response reports the exact reset time. The default starter tier is easy for an assistant to exhaust if it is doing wide, exploratory investigation across a large panel — create the assistant's key at a higher tier deliberately if you see repeated 429 RATE_LIMIT_EXCEEDED responses rather than treating the limit as something to route around.

The default starter tier (60 requests/minute) is easy for an assistant to exhaust during wide, exploratory investigation — a session that lists domains, then DNS records, then SSL status for a dozen sites can burn through it quickly. Raise the tier deliberately for keys handed to an AI client, rather than treating repeated 429s as a bug.

How Do MCP Annotations Let a Client Decide When to Ask for Confirmation?

Every one of the 404 tools in the catalogue carries a safety annotation derived from its HTTP method: readOnlyHint on every GET, destructiveHint on every DELETE. As measured today, that breaks down to 180 read-only tools, 178 mutating tools and 46 destructive tools. Clients that respect these annotations — Cursor and Codex CLI among them — use them to decide, without asking the model, when a call is safe to run automatically and when a human should confirm first. A tool that lists domains runs without friction; a tool that deletes a database is flagged so the client can put a person in the loop before it executes, independent of how confidently the model believes the action is correct.

AnnotationExample toolsWhat it means
Read-onlyList domains, get SSL status, tail logsSafe to run without confirmation; cannot change server state
MutatingCreate a database, deploy an application, restart a serviceChanges state but is generally reversible or expected as routine
DestructiveDelete a domain, drop a database, remove a backupIrreversible or high-impact; clients should confirm before executing

How Do Server Instructions Stop an Assistant From Acting Outside Its Scope?

Annotations tell the client how to behave; the server instructions sent at connection time tell the model itself the same thing, in its own context. Those instructions describe every scope family, state the safety rule for mutating and destructive tools directly, and — because the MCP server probes GET /v1/me at startup — name the exact key the session is holding, its scopes, its tier and its expiry before the first tool call happens. A read-only key is announced as read-only from message one.

Panelica API error 403 on POST /v1/domains
Message: This API key lacks the scope required for POST requests on this resource
What to do: the API key lacks the scope "domains:write" (it has: *:read, dns:write).
Retrying cannot help — ask the operator to add that scope to the key in the panel
(Settings > API Keys), or use a read-only alternative.
Panelica API error 404 on GET /v1/domains/<uuid>
Message: Not found
What to do: no such resource for this key's owner. Ids are UUIDs that must come from
a list call (e.g. GET /v1/domains, GET /v1/accounts) — never invent or reuse one
from memory. Re-list, pick the id from the result, then call again.

This is not a client-side guess about politeness — it is the model reading, in its own instructions, exactly what the credential in front of it can and cannot do, and what to do about each error class. This is verbatim output from panelica-mcp 0.5.1, and matches the same behavior observed in a real session driven by OpenAI Codex CLI: asked to attempt an action outside its granted scope, it declined and named the missing scope, without attempting the call itself.

What Changed When Scope Documentation Moved From Hand-Written to Code-Derived?

On newer panels, the scopes shown in every tool's description are pulled directly from the middleware chain that actually enforces them at runtime — including "one of scope A or B" rules and role restrictions like ROOT/ADMIN-only routes — instead of a separately maintained document that can drift out of sync with the code. During the work that produced this documentation, that code-derived pass found and corrected 32 places where the hand-written scope docs disagreed with what the middleware actually required: for example, /v1/search turned out to need no scope at all, while API key management itself requires *:*. The practical effect is that a tool's stated scope requirement is no longer a claim someone wrote down once — it is read from the same code path that will reject the request if the claim is wrong.

How Is Every Action an Assistant Takes Logged and Traced?

Every request made through an API key — whether it originates from an AI client, a script, or any other integration — passes through the panel's normal audit logging and RBAC exactly as if it had come from the dashboard. An action taken by an assistant is indistinguishable, from the audit log's point of view, from any other authenticated API call, and it can always be traced back to the specific key that performed it. If an assistant's session did something unexpected, the audit trail answers "what happened and under which key" without needing to reconstruct the conversation that led to it.

What Other Guardrails Exist Beneath the API Layer?

Scoping, signing and rate limits govern who can call what and how often; a few checks inside individual endpoints close off specific abuse paths regardless of scope. DNS record creation rejects private and reserved IP addresses for A and AAAA records, which blocks a class of server-side request forgery where a DNS entry is used to redirect internal traffic. FTP account home directories are fenced to the owning account's home, so an FTP-scoped key cannot be used to reach outside the account boundary it belongs to. Passwords generated or accepted through the API carry a minimum length of 8 characters. None of these depend on the calling client behaving well — they hold regardless of whether the caller is an AI assistant, a script, or a person.

Four Scoped Key Recipes for Common AI Workflows

Read-only auditor key

Grant *:read (or, on panels that predate the wildcard, the individual <area>:read scopes for whichever areas matter) and nothing else. This key can answer any "what is the current state of X" question — list domains, check certificate expiry, summarize server metrics, tail logs — and cannot change anything no matter how it is instructed. It is the safest possible key to hand to a new AI client on a production panel for the first time.

DNS operator key

Grant dns:read and dns:write, plus domains:read so the assistant can resolve a domain name to the id a DNS call needs. This key can list, add and update DNS records but cannot delete a domain, touch billing, or reach into databases, email or backups — a natural fit for an assistant whose job is exactly "manage DNS records," no more.

Full automation key

Grant the specific write and delete scopes a trusted, well-understood automation actually exercises — commonly domains, DNS, SSL, databases and server service control together — rather than reaching for *:* by default. Reserve *:* for a genuinely trusted embedded agent operating from a controlled environment, and prefer the narrower explicit grant everywhere else; a full-automation key is still worth an expiry and, where the calling environment has a stable IP, an IP whitelist.

Per-client key with expiry

Generate a separate key for every distinct MCP client or automation rather than sharing one key across all of them, and set expires_in at creation time for anything that is not meant to run indefinitely — a one-off migration assistant, a contractor's temporary access, an evaluation of a new AI tool. A distinct key per client also means the audit log tells you which client did what, and revoking access to one client never touches any other.

How Does This Compare to Handing Someone SSH Access?

An SSH key with shell access is effectively unscoped: whoever holds it can do close to anything the underlying Linux user can do, and there is no per-action risk label anywhere in that path — a destructive command looks exactly like a harmless one until it has already run. A Panelica API key used by an AI assistant is narrower by construction on every axis that matters here: it is limited to the External API's documented endpoints, each one gated by its own permission scope, every one individually labeled by risk class, every request individually signed and logged, and revocable in one action without touching any other credential. Scoped, signed, labeled and logged access compares favorably to a shared SSH key — the AI layer does not make the comparison worse, and done properly it makes it considerably better.

Frequently Asked Questions

Is it safe to give an AI assistant access to a production server?

It is safe in proportion to how the API key behind it is scoped, signed, time-limited and monitored — the same standard that applies to any other automated credential. Panelica's controls are designed so that answering "how scoped is this key" honestly is enough to reason about the risk.

What is the minimum scope I should grant to a brand-new AI client I have not evaluated yet?

Start with *:read (or the equivalent individual read scopes) and nothing else. A read-only key can answer questions and diagnose problems, and cannot change anything regardless of how it is prompted.

Can a scoped key see other customers' data on a multi-account panel?

No. Scopes govern which categories of action a key can perform; the key's inherited role (ROOT, ADMIN, RESELLER or USER) governs which records it can see at all, and that role boundary is enforced first, independent of scope.

What happens the instant I revoke a key?

The very next request made with that key fails immediately. There is no cached authorization and no grace period on the panel side.

Does scoping protect against a destructive call I explicitly authorized?

No — scoping limits what a key is allowed to attempt, not whether a specific authorized call within that scope is a good idea. A key with legitimate backups:restore access will still execute a restore if the client proceeds without confirmation; the destructive-tool annotation exists specifically so capable clients pause and ask before that happens.

Can a compromised MCP client host expose my panel's data to a third party?

The MCP server itself emits no telemetry and contacts no third party — it only talks to your own panel over the signed connection. If the machine running it is compromised, the exposure is limited to what the API key on that machine is scoped to do, which is exactly why narrow scoping and short expiries matter for any client running somewhere less trusted than your own workstation.

How does the panel stop a replayed request from an intercepted signature?

The HMAC signature is computed over the request's timestamp as well as its method, path and body, and the panel rejects any request whose timestamp has drifted more than five minutes from its own clock. A signature captured today cannot be replayed successfully once that window has passed.

What is the difference between a "one of A or B" scope rule and a normal single scope requirement?

Some routes accept more than one scope as sufficient — for example, a route might accept either a specific area scope or a broader wildcard that covers it. Newer panels state these "one of" rules explicitly in the tool description, derived from the actual middleware check, rather than requiring you to infer which combination works.

Do destructive tools require extra confirmation from the panel itself, independent of the client?

The panel enforces scope and role on every call regardless of client behavior. The destructive annotation is a signal to the calling client to seek confirmation before it sends the request at all — it does not by itself add a second server-side confirmation step, which is why choosing a client that respects the annotation, and scoping the key narrowly in the first place, both matter.

Can I restrict an API key to only be usable from my office or CI runner's IP address?

Yes — keys can be IP-whitelisted at creation time, restricting them to a specific address or range regardless of what scopes they carry.

Where can I see what an AI-driven session actually did on my server?

In the panel's audit log, the same place every other API-driven action is recorded, filterable by the API key identity that performed each action.

Generate a properly scoped key before connecting any AI client. Start narrow, expand only as needed, and review the destructive-tool list before trusting a session with broad write access.

Related reading on panelica.com

Security-first hosting panel

Stop bolting tools onto a legacy panel.

Panelica is a modern, security-first hosting panel — isolated services, built-in Docker and AI-assisted management, with one-click migration from any panel.

Zero-downtime migration Fully isolated services Cancel anytime
Share:
Pay once. Host forever.