Ultra-fast, configuration-driven RBAC/ABAC authorization framework for Go
goaegis is a lightweight, plug-and-play authorization library that provides powerful role-based and attribute-based access control without any authentication logic. It's designed to be authentication-agnostic, fully decoupled from user identity systems, and blazingly fast with all data held in memory.
- 🚀 Zero Dependencies on Auth - goaegis doesn't know or care about users, tokens, sessions, or authentication
- ⚡ In-Memory Performance - Everything loaded at startup, no database queries during authorization
- 📝 Configuration-First - Define your entire authorization model in YAML (or future
.aegisformat) - 🌐 Remote Config Loading - Load configs from GitHub, S3, HTTP, or any custom source via addons
- 🔥 Hot Reload - Update configs without restarting your application
- 🔄 Role Inheritance - Roles can inherit from other roles automatically
- 🎯 Flexible Permissions - Support for allow/deny effects with deny-override semantics
- ✅ Comprehensive Validation - Catches duplicates, unknown references, circular inheritance, and more at load time
- 🔌 Addon System - Extend functionality with comprehensive lifecycle hooks
- 🌐 Framework Agnostic - Works with any Go web framework (middleware included for common ones)
- 📦 Multi-File Configs - Load from a single file or arbitrarily nested directory structure
go get github.com/goaegis/goaegis-coreCreate a config.yaml file:
resources:
posts:
name: posts
type: collection
comments:
name: comments
type: collection
roles:
viewer:
name: viewer
permissions:
- resource: posts
actions: [read]
effect: allow
author:
name: author
inherits: [viewer]
permissions:
- resource: posts
actions: [create, update]
effect: allow
subjects:
user:alice:
id: user:alice
roles: [viewer]
user:bob:
id: user:bob
roles: [author]package main
import (
"log"
aegis "github.com/goaegis/goaegis-core/aegis/core"
)
func main() {
// Initialize goaegis
authz := aegis.New()
// Load configuration
if err := authz.LoadConfig("./config.yaml"); err != nil {
log.Fatal(err)
}
// Check authorization
allowed, err := authz.Can("user:alice", "posts", "read", nil)
if err != nil {
log.Fatal(err)
}
if allowed {
log.Println("Alice can read posts")
} else {
log.Println("Alice cannot read posts")
}
}goaegis-core/
├── aegis/ # CORE LIBRARY
│ ├── config/ # Configuration models and YAML loader
│ ├── core/ # Core Aegis instance and API
│ ├── engine/ # Authorization evaluation engine
│ ├── addons/ # Addon system interfaces
│ └── middleware/ # HTTP middleware helpers
└── examples/ # Example configurations and usage
Defines the structure of your authorization model:
- Resources - Things to be protected (posts, comments, users, etc.)
- Roles - Named sets of permissions with inheritance support
- Subjects - Entities performing actions (users, service accounts)
- Policies - Future: attribute-based rules (ABAC)
The evaluation engine that:
- Resolves role inheritance recursively
- Aggregates permissions from all roles
- Applies deny-override semantics
- Matches resources and actions (with wildcard support)
The main Aegis struct providing:
LoadConfig(path)- Load and validate YAML configurationCan(subject, resource, action, context)- Authorization checkUse(addon)- Register addons
Automatic validation catches errors at load time:
- Duplicate resources, roles, or subjects
- Unknown resource references in permissions
- Unknown role references in subjects/inheritance
- Circular role inheritance
- Invalid permission effects
- Missing required fields
- Name/key consistency
Extensibility interface:
type Addon interface {
Name() string
Init(core any) error
OnBeforeConfigLoad(path string) (ConfigSource, error)
OnConfigValidate(cfg *config.Config) (*config.Config, error)
OnConfigLoad(cfg *config.Config) error
OnAuthorize(ctx *Context) (Decision, error)
Shutdown() error
}resources:
resource_id:
name: resource_id
type: collection|singleton
meta:
description: "What this resource is"
custom_field: "anything"roles:
role_name:
name: role_name
inherits: [parent_role] # Optional inheritance
permissions:
- resource: resource_id
actions: [read, write, delete]
effect: allow|denysubjects:
subject_id:
id: subject_id
roles: [role1, role2]
meta:
email: user@example.com
department: engineering- allow (default) - Grants access to specified actions
- deny - Explicitly denies access (overrides allows)
Evaluation Logic:
- Collect all permissions from subject's roles (including inherited)
- Check for matching resource/action combinations
- If any permission has
effect: deny→ DENY - If any permission has
effect: allow→ ALLOW - Default → DENY
// In your authentication middleware
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. Extract/verify user identity (your auth logic)
subjectID := getUserIDFromToken(r)
// 2. Check authorization with goaegis
allowed, _ := authz.Can(subjectID, "resource", "action", nil)
if !allowed {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}context := map[string]any{
"ip_address": "192.168.1.1",
"time_of_day": "business_hours",
"risk_score": 0.2,
}
allowed, err := authz.Can("user:alice", "sensitive_data", "read", context)roles:
admin:
name: admin
permissions:
- resource: "*" # All resources
actions: ["*"] # All actions
effect: allowThe loader supports arbitrary directory nesting. Organize your config files however you want:
config/
├── resources.yaml
├── roles.yaml
└── subjects.yaml
Or with nested subdirectories:
config/
├── resources.yml
├── roles/
│ ├── role-a.yaml
│ ├── role-b.yaml
│ └── admin/
│ └── super-admin.yaml
└── subjects.yaml
The loader recursively finds all .yml, .yaml, and .aegis files:
authz.LoadConfig("./config") // Loads all files recursivelyBy default, goaegis loads configs from the filesystem. For remote sources (S3, GitHub, HTTP, etc.), use addons from separate repositories. Each remote source needs different SDKs and authentication, so they're packaged separately.
// Filesystem (default)
authz := aegis.New()
authz.LoadConfig("./config")
// With remote source addon (see related projects below)
authz.Use(remoteAddon)
authz.LoadConfigFromAddon() // Cleaner API for addon sourcesManual Hot Reload:
// Reload config manually (useful for admin endpoints)
if err := authz.ReloadConfig(); err != nil {
log.Printf("Reload failed: %v", err)
}Addons extend goaegis with custom functionality including remote config loaders, monitoring, and custom authorization logic.
See ADDON_HOOKS.md for comprehensive documentation on:
- Creating addons
- Lifecycle hooks and interfaces
- Hot reload implementation
- Complete examples
Example usage:
// Register addons
authz := aegis.New()
authz.Use(myAddon)
defer authz.Shutdown()
// Load config
authz.LoadConfig("./config.yaml")- API Authorization - Protect REST/GraphQL endpoints
- Multi-Tenant SaaS - Organization-level access control
- Microservices - Consistent authorization across services
- Admin Panels - Role-based UI/feature access
- Service-to-Service - Machine identity authorization
- YAML configuration loader (local & remote)
- RBAC engine with role inheritance
- Addon system with comprehensive lifecycle hooks
- Hot reload support
- Comprehensive validation system
- Complete test suite (51+ tests)
-
.aegisfile format with custom parser - ABAC policy evaluation engine
Contributions are welcome! Please feel free to submit a Pull Request.
MIT License - see LICENSE file for details
All servers, UIs, and remote loaders are separate addon repositories:
Remote Config Loaders:
- goaegis-github - Load configs from GitHub with hot reload (coming soon)
- goaegis-s3 - Load configs from AWS S3 with hot reload (coming soon)
Servers & UIs:
- goaegis-server - Standalone HTTP server addon (coming soon)
- goaegis-ui - Web interface addon for managing authorization (coming soon)
Development Tools:
- goaegis-lsp - Language server addon for
.aegisfiles (coming soon)
goaegis-core is a pure library with no server code. All extensions are implemented as addons.
Built with ❤️ for the Go community
