Staff Permissions
Declare granular staff permissions in VitNode plugins, enforce them on Hono routes, and gate AdminCP UI controls.
VitNode separates staff into two groups: Moderators and Administrators. Plugins declare granular permissions that restrict what staff members or roles can access.
New Admin APIs Require Staff Permissions
Routes under /admin/ are automatically gated by the admin session, but you must declare an explicit staff permission tuple to restrict specific actions.
Quick start
1. Declare Permissions in Plugin API Config
import { buildApiPlugin } from "@vitnode/core/api/lib/plugin"
export const blogApiPlugin = () =>
buildApiPlugin({
pluginId: "@vitnode/blog",
permissionStaff: {
admin: {
posts: [
"can_view",
{ permission: "can_delete", dependsOn: ["can_view"] },
],
},
},
})2. Enforce on Hono Route
Add adminStaffPermission to automatically return 403 Forbidden if unauthorized:
export const deletePostRoute = buildRoute({
pluginId: "@vitnode/blog",
adminStaffPermission: { module: "posts", permission: "can_delete" },
route: {
method: "delete",
path: "/{id}",
responses: {
200: { description: "Post deleted" },
403: { description: "Forbidden" },
},
},
handler: async (c) => {
// Execution only reaches here if caller has permission
},
})Add Permissions Step by Step
1. Define Translation Labels
Add flat labels in plugins/blog/src/locales/en.json:
{
"@vitnode/blog": { "title": "Blog" },
"@vitnode/blog:posts": "Articles",
"@vitnode/blog:posts:can_view": "View articles list",
"@vitnode/blog:posts:can_create": "Create articles",
"@vitnode/blog:posts:can_delete": "Delete articles"
}2. Gate UI Controls
Hide buttons or panels from unauthorized staff with AdminStaffPermissionGate:
import { AdminStaffPermissionGate } from "@vitnode/core/components/staff-permission/provider"
export const DeletePostButton = () => (
<AdminStaffPermissionGate
plugin="@vitnode/blog"
module="posts"
permission="can_delete"
>
<button className="text-destructive">Delete</button>
</AdminStaffPermissionGate>
)Or check permissions imperatively with useStaffPermissions:
const { hasPermission } = useStaffPermissions()
const canDelete = hasPermission({ module: "posts", permission: "can_delete" })3. Gate Sidebar Navigation
Attach permission to navigation items in src/admin/nav.tsx:
export const adminNav: PluginAdminNav = {
nav: [
{
id: "posts",
href: "/admin/blog/posts",
icon: <ListIcon />,
permission: { module: "posts", permission: "can_view" },
},
],
}4. Grant Permissions in AdminCP
- Go to Staff → Administrators (
/admin/core/staff/admins). - Select a User or Role and click Edit.
- Choose Restricted, toggle the desired permissions, and click Save Changes.
Permission Dependencies
When a permission depends on another (e.g. creating posts requires viewing them), declare dependsOn:
permissionStaff: {
admin: {
posts: [
"can_view",
{ permission: "can_create", dependsOn: ["can_view"] },
{ permission: "can_delete", dependsOn: ["can_view"] },
],
},
}The AdminCP permission editor automatically disables child toggles until prerequisites are checked.