@ak-network/permkit
v0.1.2
Published
<!-- # PermKit
Maintainers
Readme
git commit -m "🚀 publish(npm): release v0.1.2"
git push origin main🧩 @ak-network/permkit
A flexible permission engine for SvelteKit apps supporting RBAC, PBAC, and ABAC, with hot-reloadable rules and a clean API for server and client.
🚀 Installation
npm add @ak-network/permkit
# or
pnpm add @ak-network/permkitPeer dependencies:
"svelte": "^5.0.0"💡 Core Concepts
permkit supports three permission models:
1. RBAC (Role-Based Access Control)
- Map static roles to permissions:
const roles = {
admin: ['posts:read', 'posts:write'],
editor: ['posts:read', 'posts:write'],
viewer: ['posts:read']
};- Roles are expanded at login into permissions.
2. PBAC (Pattern-Based Access Control)
- Supports wildcard-aware string matching:
posts:* // matches all post permissions
billing:invoice:* // matches all invoice actions- Async overrides allowed if needed.
3. ABAC (Attribute-Based Access Control)
- Async functions with signature:
condition: async ({ user, resource, env, permission }) => boolean- Declarative JSON rules, prioritized by number (lowest first).
- Any explicit
denyshort-circuits evaluation. allowis returned only if a rule explicitly grants it.
Example ABAC rules:
const rules = [
{
name: 'allow-edit-own-post',
effect: 'allow',
priority: 1,
condition: (ctx) => ctx.user.id === ctx.resource?.ownerId,
},
{
name: 'deny-edit-others-post',
effect: 'deny',
priority: 2,
condition: (ctx) => ctx.user.id !== ctx.resource?.ownerId,
},
{
name: 'office-hours-only',
effect: 'allow',
priority: 3,
condition: (ctx) => {
const hour = ctx.env?.time?.getHours() ?? 0;
return hour >= 9 && hour <= 17;
}
}
];♻️ Hot-Reloading Rules
await engine.replaceRules(await loadRulesFromDB());- SvelteKit handle hook can auto-update rules periodically or on events.
- Listen to changes:
engine.on('rules-updated', (newRules) => {
console.log("Permission rules hot-reloaded");
});💻 Server-Side Usage
1. Create Engine
import { createPermissionEngine } from "@ak-network/permkit/server";
import { db } from "$lib/db";
const engine = await createPermissionEngine({
roles,
ruleSource: {
async load() { return await db.getPermissionRules(); },
async watch(cb) { db.on("permission_rule_change", cb); }
},
defaultDeny: true
});2. SvelteKit Handle Hook
import { handlePermissions } from "@ak-network/permkit/sveltekit/handle";
export const handle = handlePermissions({
engine,
getUser: async (event) => event.locals.session?.user ?? null,
getEnv: async (event) => ({
ip: event.getClientAddress(),
time: new Date()
})
});3. Permission Check Endpoint
src/routes/__permissions/check/+server.ts:
import type { RequestHandler } from "@sveltejs/kit";
export const POST: RequestHandler = async ({ request, locals }) => {
if (!locals.user) return new Response(JSON.stringify({ allowed: false, reason: "not-logged-in" }), { status: 200, headers: { "content-type": "application/json" } });
const { permission, resource } = await request.json();
const allowed = await locals.can(permission, resource);
return new Response(JSON.stringify({ allowed }), { headers: { "content-type": "application/json" } });
};4. Server-Side Guard Example
import { guard } from "@ak-network/permkit/sveltekit";
export const load = guard(
"posts.write",
async (event) => ({ secret: "you can edit posts" }),
(event) => ({ ownerId: event.locals.user?.id })
);🖥 Client-Side Usage
1. Permission Store
import { permissionStore } from '@ak-network/permkit/client';
export const perms = permissionStore();2. Check Permissions
if ($perms.can("posts:write", { ownerId: 123 })) {
// show action
}3. <Can> Component
<Can action="posts:update" {resource}>
<button>Edit</button>
</Can>🧠 Developer Notes
- ABAC rules are evaluated after PBAC/RBAC, so a matching
allowin ABAC can override PBAC. - Hot-reload supports DB triggers or polling.
- Use
@internalfor internal types to prevent leaking implementation in docs.
🧪 Example Rules
const roles = {
admin: ['*'],
editor: ['posts:read', 'posts:write'],
viewer: ['posts:read']
};
const rules = [
{
name: 'allow-edit-own-post',
effect: 'allow',
priority: 1,
condition: (ctx) => ctx.user.id === ctx.resource?.ownerId,
},
{
name: 'deny-edit-others-post',
effect: 'deny',
priority: 2,
condition: (ctx) => ctx.user.id !== ctx.resource?.ownerId,
}
];⚖️ License
MIT
📖 Documentation
- Full API docs are generated via TypeDoc:
pnpm docs:create- Docs are output to
/docsand categorized:Shared,Server,Client,Types.
