Skip to content

Permission System

Solar Network uses a fine-grained, attribute-based permission model. Every mutating endpoint declares one or more [AskPermission("key")] attributes, and the middleware checks the actor's permission nodes before allowing the request.

Architecture

Client request
  → [Authorize] (authentication)
  → AskPermissionMiddleware (authorization)
    → OAuth scope gate (if OAuth session)
    → Superuser bypass
    → Permission node lookup (gRPC or local DB)
  → Controller action

Two middleware implementations exist:

Middleware Used by Backend
RemotePermissionMiddleware Sphere, Passport, Messager, Develop, Wallet, Ring gRPC PermissionService
LocalPermissionMiddleware Padlock Direct EF Core query

Both enforce the same logic: iterate all [AskPermission] attributes, check each key against the actor's permission nodes, return 403 on first failure.

Permission Keys

Convention: {domain}.{resource}.{action}

Examples: - posts.create - chat.messages.delete - wallets.balance.modify

Full list

Domain Keys
Accounts accounts.view, accounts.manage, accounts.delete, accounts.statuses.create, accounts.statuses.update, accounts.connections.view
Auth & Security auth.sessions.manage, auth.factors.manage, auth.api.keys.manage, auth.apps.authorize, auth.recover
Account account.contacts.manage, account.devices.manage, account.authorized.apps.manage
Chat chat.create, chat.update, chat.delete, chat.messages.create, chat.messages.update, chat.messages.delete, chat.messages.react, chat.members.manage, chat.members.kick, chat.members.timeout, chat.invites.manage, chat.e2ee.manage, chat.groups.manage, chat.pins.manage, chat.read.all, chat.sync, chat.call.start, chat.call.end, chat.call.invite, chat.call.kick, chat.call.mute
Posts posts.view, posts.create, posts.update, posts.delete, posts.publish, posts.react, posts.boost, posts.moderate, posts.lock, posts.bookmark, posts.award, posts.sponsor, posts.pin, posts.batch.delete, posts.batch.visibility
Collections post.collections.create, post.collections.update, post.collections.delete, post.collections.posts.manage
Categories & Tags post.categories.manage, post.categories.subscribe, posts.tags.create, posts.tags.update, posts.tags.assign, posts.tags.claim, posts.tags.protect, posts.tags.event
Subscriptions post.subscriptions.manage
Publishers publishers.create, publishers.update, publishers.delete, publishers.members.manage, publishers.invites.manage, publishers.features.manage, publishers.fediverse.manage, publishers.domains.manage, publishers.rewards.settle, publishers.rewards.resettle, publishers.subscriptions.manage
Timelines timelines.feedback, timelines.weights.manage, timelines.reset
Fediverse fediverse.moderation.rules.manage, fediverse.moderation.check, fediverse.keys.manage
Stickers stickers.packs.create, stickers.packs.update, stickers.packs.delete, stickers.packs.own, stickers.packs.order, stickers.create, stickers.update, stickers.delete, stickers.content.update
Surveys surveys.create, surveys.update, surveys.delete, surveys.publish, surveys.archive, surveys.clone, surveys.answer, surveys.subscribe
Wallet wallets.manage, wallets.create, wallets.delete, wallets.balance.modify, wallets.funds.manage, wallets.transactions.manage, wallets.transfer.requests.manage, wallets.public.id.manage
Orders orders.create, orders.update, orders.pay, orders.view, orders.payouts.manage
Subscriptions subscriptions.create, subscriptions.cancel, subscriptions.activate, subscriptions.order.manage, subscriptions.checkout, subscriptions.groups.manage, subscription.gifts.purchase, subscription.gifts.redeem, subscription.gifts.send, subscription.gifts.cancel
Notifications notifications.send, notifications.put, notifications.read.all, notifications.preferences.manage, notifications.subscriptions.manage, notifications.sop.subscribe
Social Credits credits.validate.perform, credits.manage
Presence presences.scan, presences.manage, presences.activity.manage, presences.artwork.manage
Relationships relationships.create, relationships.update, relationships.delete, relationships.friends.manage, relationships.block.manage, relationships.mute.manage, relationships.close.friends.manage, relationships.alias.manage, relationships.inspect, relationships.sync
Realms realms.create, realms.update, realms.delete, realms.invites.manage, realms.members.manage, realms.labels.manage, realms.boosts.manage, realms.permissions.manage
Notable Days notable.days.create, notable.days.update, notable.days.delete
Calendar calendar.events.create, calendar.events.update, calendar.events.delete, calendar.subscriptions.manage, calendar.checkin.manage
E2EE e2ee.keys.manage, e2ee.mls.manage, e2ee.devices.manage
Developers developers.create, developers.manage, custom.apps.create, custom.apps.update, custom.apps.delete, custom.apps.secrets.manage, bot.accounts.create, bot.accounts.update, bot.accounts.delete, bot.accounts.keys.manage, bot.accounts.chat.manage, app.products.create, app.products.update, app.products.delete, dev.projects.create, dev.projects.update, dev.projects.delete, mini.apps.create, mini.apps.update, mini.apps.delete, quotas.manage
Permissions permissions.check, permissions.manage, permissions.groups.check, permissions.groups.manage, permissions.cache.manage
Punishments punishments.view, punishments.create, punishments.update, punishments.delete

OAuth Scope Filtering

When a request comes from an OAuth session, the token's scopes are checked against the required permission keys:

Scope Effect
* Bypasses all permission checks
posts.* Matches any key with the posts. prefix (e.g. posts.create, posts.update)
posts.create Matches only the exact key

This is handled by PermissionScopeGate.GetMatchedPermissionScope().

Permission Nodes

Each permission is stored as an SnPermissionNode:

Field Description
Actor user:<id> or group:default
Key Permission key string
Value JSONB (boolean true for simple grants)
GroupId Optional permission group reference
ExpiredAt Optional TTL
AffectedAt Optional activation time

Permission Groups

SnPermissionGroup + SnPermissionGroupMember let you assign a set of permissions to multiple actors at once. The default group is seeded on first migration and grants all registered permission keys to every member.

Punishment-Based Blocking

SnAccountPunishment with Type = PermissionModification can block specific permission keys for an actor. Blocked permissions take precedence over granted nodes, including group memberships.

Well-Known Endpoint

GET /.well-known/permissions

Returns all registered permission keys. Public, no auth required.

{
  "count": 247,
  "permissions": [
    { "key": "accounts.connections.view", "name": "AccountsConnectionsView" },
    { "key": "accounts.delete", "name": "AccountsDeletion" }
  ]
}