Axiom est une plateforme d'automatisation et d'intelligence artificielle pour développeurs. Il combine:
- Core Go ultra-modulaire avec Event Bus asynchrone
- Sovereign API : contrôle d'accès basé sur les permissions (L0→L3)
- Support multi-provider IA : Ollama local, OpenAI, Mistral, Anthropic, Groq, llama_cpp
- FileSystem Handler avec protection path-traversal
- Window Orchestrator pour UI native (Wails v2)
Version : v0.2.0-alpha | Status : Production-Ready Core
- Installation & Dépendances
- Configuration
- Lancer Axiom
- Architecture
- Créer un Module
- API & Bus d'Événements
- Sovereign API (Système de Permissions)
- Module IA — Multi-Provider
- Troubleshooting
- Go 1.22+ (télécharger)
- Git
- Optionnel : Wails v2 pour UI native
- Optionnel : Ollama pour IA locale
# Clone le repository
git clone https://github.com/axiom-ide/axiom.git
cd axiom
# Télécharge les dépendances Go (google/uuid uniquement)
go mod download
go mod tidy
# Test compilation
go build -o axiom ./# macOS / Linux
curl https://ollama.ai/install.sh | sh
# Lancer Ollama
ollama serve
# Dans un autre terminal, télécharger un modèle (par défaut: mistral:7b)
ollama pull mistral:7b
ollama pull llama2:7b
ollama pull neural-chat:7bURL par défaut : http://localhost:11434
# Cloner llama.cpp
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make
# Lancer le serveur
./server -m /path/to/model.gguf -c 4096 -t 8
# Axiom se connectera à http://localhost:8000# Créer un compte : https://console.mistral.ai
# Générer une clé API
export AXIOM_AI_KEY="your-mistral-api-key"# Créer un compte : https://platform.openai.com
# Générer une clé API
export AXIOM_AI_KEY="sk-your-openai-api-key"# Créer un compte : https://console.anthropic.com
# Générer une clé API
export AXIOM_AI_KEY="sk-ant-your-anthropic-api-key"# Créer un compte : https://console.groq.com
# Générer une clé API
export AXIOM_AI_KEY="gsk_your-groq-api-key"Axiom charge la config dans cet ordre :
.axiom/config.json(local à votre projet)axiom.config.json(root du projet)~/.axiom/config.json(home)- Variables d'environnement (override tout)
{
"core": {
"modules_dir": "./modules",
"log_level": "info",
"bus_buffer_size": 128,
"workspace_dir": "."
},
"ui": {
"default_theme": "dark",
"window_width": 1400,
"window_height": 900,
"font_size": 14,
"tab_size": 4,
"word_wrap": false
},
"ai": {
"provider": "ollama",
"model_id": "mistral:7b",
"base_url": "http://localhost:11434",
"api_key": "",
"max_tokens": 2048,
"temperature": 0.2,
"timeout_secs": 60
},
"filesystem": {
"watch_enabled": true,
"max_file_size_mb": 50,
"ignore_patterns": [".git", "node_modules", ".axiom_cache"],
"backup_on_write": false
},
"security": {
"require_approval_for_l2": true,
"audit_log_max_entries": 1000,
"allow_external_modules": false
}
}# Core
export AXIOM_MODULES_DIR="./modules"
export AXIOM_LOG_LEVEL="debug"
export AXIOM_WORKSPACE="/path/to/workspace"
export AXIOM_DEBUG="1" # Force log_level=debug
export AXIOM_BUS_BUFFER="256"
# AI
export AXIOM_AI_PROVIDER="ollama" # ollama|llama_cpp|mistral|openai|anthropic|groq
export AXIOM_AI_MODEL="mistral:7b"
export AXIOM_AI_BASE_URL="http://localhost:11434"
export AXIOM_AI_KEY="your-api-key"
# UI
export AXIOM_THEME="dark"# Compiler et lancer
go run ./main.go
# Ou avec le binaire compilé
go build -o axiom ./
./axiomSortie attendue :
╔═══════════════════════════════════════════════╗
║ █████╗ ██╗ ██╗██╗ ██████╗ ███╗ ███╗ ║
║ ██╔══██╗╚██╗██╔╝██║██╔═══██╗████╗ ████║ ║
║ ███████║ ╚███╔╝ ██║██║ ██║██╔████╔██║ ║
║ ██╔══██║ ██╔██╗ ██║██║ ██║██║╚██╔╝██║ ║
║ ██║ ██║██╔╝╚██╗██║╚██████╔╝██║ ╚═╝ ██║ ║
║ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ║
║ ║
║ Modular Software Engineering Platform ║
║ v0.2.0-alpha · Sovereign API Active ║
║ Multi-Provider AI Support (Ollama, OpenAI...) ║
╚═══════════════════════════════════════════════╝
[INFO] axiom: initializing engine, version=0.2.0-alpha
[INFO] filesystem: handler ready, workspace=/path/to/workspace
[INFO] axiom: starting module discovery...
[INFO] registry: module loaded successfully, id=ai-assistant, clearance=L2_Architect
[INFO] axiom: all modules processed, total=1, active=1
[INFO] axiom: engine is ready ✓
Prérequis : Wails v2 installé
# Installer Wails
go install github.com/wailsapp/wails/v2/cmd/wails@latest
# Ajouter le build tag et compiler
wails build -platform windows/amd64
# ou pour macOS
wails build -platform darwin
# ou pour Linux
wails build -platform linux# Lancer tous les tests
go test ./...
# Avec couverture
go test -cover ./...
# Spécifique à un package
go test ./core/bus -v
go test ./core/security -v
go test ./core/filesystem -vaxiom/
├── api/ # Contrat public (Event Types, Topics)
│ └── events.go # Topics: file.*, ui.*, ai.*, security.*
│
├── core/
│ ├── bus/ # Event Bus asynchrone
│ ├── config/ # Config hiérarchique
│ ├── engine/ # Moteur central (orchestration)
│ ├── filesystem/ # FileSystem Handler (sécurisé)
│ ├── module/ # Contrat des modules
│ ├── orchestrator/ # Window Orchestrator (UI)
│ ├── registry/ # Module Registry & Discovery
│ └── security/ # Sovereign API (permissions)
│
├── adapters/
│ └── wails/ # Adapter Wails v2
│
├── modules/
│ ├── ai-assistant/ # Module IA (Multi-provider)
│ ├── file-explorer/ # File Explorer (L1)
│ └── theme-manager/ # Theme Manager (L2)
│
├── pkg/
│ ├── uid/ # UUID v4 Generator
│ └── logger/ # Logging utilities
│
└── main.go # Point d'entrée + démo
Module A
↓
engine.Dispatch(moduleID, topic, payload)
↓
security.Authorize(moduleID, topic) ← Vérification des permissions
↓ (OK)
bus.Publish(event)
↓
topicRouter.dispatch() → Handler goroutines
↓
Module B, C, ... reçoivent l'événement
| Level | Name | Permissions | Modules |
|---|---|---|---|
| L0 | Observer | file.read, system.ready |
Theme Explorer |
| L1 | Editor | L0 + file.create, file.write, file.delete |
File Explorer |
| L2 | Architect | L1 + ui.*, ai.command |
AI Assistant, Theme Manager |
| L3 | Sovereign | L2 + system.shutdown, system.module.* |
Engine, Security Manager, Filesystem |
mkdir -p modules/my-custom-module
cd modules/my-custom-module
# Créer les fichiers
touch manifest.json my_module.go my_module_test.gomodules/my-custom-module/manifest.json :
{
"id": "my-custom-module",
"name": "My Custom Module",
"version": "1.0.0",
"description": "Un module exemple avec permutations L1",
"author": "Axiom Developer",
"clearance_level": 1,
"entry_point": "plugin.so",
"ui_slots": ["sidebar"],
"subscriptions": [
"file.created",
"file.deleted",
"system.ready"
],
"capabilities": [
"read_files",
"create_files",
"write_files"
],
"enabled": true
}Règles du manifest :
id: identifiant unique (kebab-case)clearance_level: 0 (L0), 1 (L1), 2 (L2), 3 (L3)subscriptions: Topics que le module écoutecapabilities: Commentaire sur ce que le module peut faireenabled:truepour charger automatiquement
modules/my-custom-module/my_module.go :
package mycustommodule
import (
"context"
"log/slog"
"github.com/axiom-ide/axiom/api"
"github.com/axiom-ide/axiom/core/module"
"github.com/axiom-ide/axiom/core/security"
)
type MyCustomModule struct {
module.BaseModule
config map[string]interface{}
}
// New crée une instance du module
func New(logger *slog.Logger) *MyCustomModule {
return &MyCustomModule{
BaseModule: module.NewBase(
"my-custom-module",
"My Custom Module",
security.L1, // Clearance Level
logger,
),
}
}
// Init est appelé au démarrage du moteur
func (m *MyCustomModule) Init(ctx context.Context, d module.Dispatcher, s module.Subscriber) error {
m.BaseInit(ctx, d, s)
// S'abonner aux fichiers créés
m.On(api.TopicFileOpened, func(ev api.Event) {
m.Logger().Info("my-module: fichier ouvert",
slog.String("source", ev.Source),
)
})
// S'abonner au démarrage du système
m.On(api.TopicSystemReady, func(ev api.Event) {
m.Logger().Info("my-module: système prêt")
// Émettre une action (avec vérification des permissions)
if err := m.Emit(api.TopicFileRead, api.PayloadFileRead{
Path: "config.json",
}); err != nil {
m.Logger().Error("my-module: erreur dispatch", slog.String("error", err.Error()))
}
})
m.Logger().Info("my-module: initialized")
return nil
}
// Stop arrête proprement le module
func (m *MyCustomModule) Stop() error {
return m.BaseStop()
}
// Custom methods
func (m *MyCustomModule) DoSomething() {
m.Logger().Info("my-module: doing something!")
}modules/my-custom-module/my_module_test.go :
package mycustommodule_test
import (
"context"
"log/slog"
"os"
"testing"
"github.com/axiom-ide/axiom/core/bus"
"github.com/axiom-ide/axiom/core/module"
mycustommodule "github.com/axiom-ide/axiom/modules/my-custom-module"
)
var testLogger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelError}))
func TestModuleInit(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Créer un event bus
eb := bus.New(ctx, 64, testLogger)
// Créer le module
mod := mycustommodule.New(testLogger)
// Initialiser
dispatcher := &testDispatcher{eb: eb}
subscriber := &testSubscriber{eb: eb}
if err := mod.Init(ctx, dispatcher, subscriber); err != nil {
t.Fatalf("Init failed: %v", err)
}
if mod.ID() != "my-custom-module" {
t.Errorf("expected ID 'my-custom-module', got '%s'", mod.ID())
}
}
type testDispatcher struct{ eb *bus.EventBus }
func (d *testDispatcher) Dispatch(moduleID string, topic interface{ String() string }, payload interface{}) error {
return nil
}
type testSubscriber struct{ eb *bus.EventBus }
func (s *testSubscriber) Subscribe(topic interface{ String() string }, handler func(interface{ GetTopic() interface{ String() string } })) string {
return ""
}
func (s *testSubscriber) Unsubscribe(topic interface{ String() string }, subID string) {}Dans main.go :
import (
mycustommod "github.com/axiom-ide/axiom/modules/my-custom-module"
)
// ...
// Ajouter au module runner
mod := mycustommod.New(slog.Default())
runner.Register(mod)Ou automatiquement via le Registry (si enabled: true dans manifest.json).
api.TopicFileCreate // {"path": "...", "content": "..."}
api.TopicFileRead // {"path": "..."}
api.TopicFileWrite // {"path": "...", "content": "...", "append": bool}
api.TopicFileDelete // {"path": "..."}
api.TopicFileOpened // Publié par le filesystem après lectureapi.TopicUIOpenPanel // {"panel_id": "...", "title": "...", "position": "..."}
api.TopicUIClosePanel // {"panel_id": "..."}
api.TopicUISetTheme // {"theme_id": "dark|light|monokai"}
api.TopicUINewWindow // Créer une nouvelle fenêtreapi.TopicAICommand // {"raw_command": "...", "parsed_topic": "...", "parsed_payload": {...}}
api.TopicAIResponse // Réponse du moteurapi.TopicSystemReady // Le moteur est prêt
api.TopicSystemShutdown // Arrêt imminent
api.TopicModuleLoaded // Un module s'est chargé
api.TopicModuleError // Erreur lors du chargementapi.TopicSecurityDenied // Action refusée (permissions insuffisantes)
api.TopicSecurityAudit // Événement d'audit// Envoyer un événement via le module
m.Emit(api.TopicFileCreate, api.PayloadFileCreate{
Path: "output.go",
Content: "package main\n\nfunc main() {}",
})// Écouter un événement
m.On(api.TopicSystemReady, func(ev api.Event) {
// ev.Topic, ev.Source, ev.Payload, ev.Timestamp
m.Logger().Info("System ready!", slog.String("source", ev.Source))
})Chaque Topic a un niveau requis :
// Dans security/clearance.go
var topicRequirements = map[string]ClearanceLevel{
"file.read": L0,
"file.create": L1,
"file.write": L1,
"ui.panel.open": L2,
"system.shutdown": L3,
}Quand un module appelle m.Emit() :
- Le Security Manager vérifie le clearance du module
- Compare avec le niveau requis du Topic
- Si insuffisant → SecurityError, l'action est bloquée
- Sinon → L'événement est publié normalement
// Module avec L1
m.Emit(api.TopicFileCreate, ...) // ✓ OK (L1 >= L1 requis)
m.Emit(api.TopicUISetTheme, ...) // ✗ DENIED (L1 < L2 requis)
m.Emit(api.TopicSystemShutdown, ...) // ✗ DENIED (L1 < L3 requis)secMgr.RegisterModule("my-module", security.L1)
secMgr.RegisterModule("admin-panel", security.L3)| Provider | Setup | Coût | Vitesse | Qualité |
|---|---|---|---|---|
| ollama | Local + gratuit | 0€ | Rapide (GPU) | Good (Mistral, Llama) |
| llama_cpp | Local + gratuit | 0€ | Très rapide | Good |
| mistral | API Cloud | ~€0.15/1M tokens | Très rapide | Excellent |
| openai | API Cloud | ~€0.03/1K tokens (GPT-4) | Moyen | Meilleur |
| anthropic | API Cloud | ~€0.008/1K tokens | Moyen | Excellent (Claude) |
| groq | API Cloud (beta) | Gratuit | Ultra-rapide | Bon |
# Démarrer Ollama
ollama serve
# Télécharger un modèle
ollama pull mistral:7b
# Config Axiom
export AXIOM_AI_PROVIDER="ollama"
export AXIOM_AI_MODEL="mistral:7b"
export AXIOM_AI_BASE_URL="http://localhost:11434"# Démarrer le serveur
./server -m model.gguf
# Config Axiom
export AXIOM_AI_PROVIDER="llama_cpp"
export AXIOM_AI_BASE_URL="http://localhost:8000"# Clé API depuis https://console.mistral.ai
export AXIOM_AI_PROVIDER="mistral"
export AXIOM_AI_MODEL="mistral-small" # ou mistral-medium, mistral-large
export AXIOM_AI_KEY="your-mistral-api-key"# Clé API depuis https://platform.openai.com
export AXIOM_AI_PROVIDER="openai"
export AXIOM_AI_MODEL="gpt-4" # ou gpt-3.5-turbo
export AXIOM_AI_KEY="sk-..."# Clé API depuis https://console.anthropic.com
export AXIOM_AI_PROVIDER="anthropic"
export AXIOM_AI_MODEL="claude-3-sonnet-20240229"
export AXIOM_AI_KEY="sk-ant-..."# Clé API depuis https://console.groq.com
export AXIOM_AI_PROVIDER="groq"
export AXIOM_AI_MODEL="mixtral-8x7b-32768"
export AXIOM_AI_KEY="gsk_..."// Dans un module
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
result, err := aiMod.Query(ctx, "Create a Go function that sorts arrays", "")
if err != nil {
m.Logger().Error("AI query failed", slog.String("error", err.Error()))
return
}
for _, cmd := range result.Commands {
m.Logger().Info("AI command", slog.String("topic", string(cmd.Topic)))
// Dispatcher la commande via le moteur
m.Emit(cmd.Topic, cmd.Payload)
}Le module IA extrait les commandes du format :
I'll create a sorting function for you.
<axiom:command>FILE_CREATE sort.go package main
func Sort(arr []int) {
// implementation
}</axiom:command>
Done! The file is ready.
Commandes supportées :
FILE_CREATE <path> <content>FILE_WRITE <path> <content>FILE_READ <path>UI_SET_THEME <theme_id>UI_OPEN_PANEL <panel_id> <title>
❌ ERROR: registry: module load failed, dir=ai-assistant, error=manifest: cannot read...
Solution :
- Vérifier que le
manifest.jsonexiste et est valide - Valider le JSON :
jq . modules/ai-assistant/manifest.json - Vérifier l'ID du module dans le manifest
🔒 SECURITY DENIED: module 'my-module' (clearance=L1) attempted to publish on 'ui.panel.open' (requires=L2)
Solution :
- Augmenter le
clearance_leveldans le manifest.json - Ou réduire les topics que le module utilise
❌ ai: LLM query failed: ollama HTTP error: connection refused
Solution :
# Vérifier qu'Ollama tourne
ps aux | grep ollama
# Relancer Ollama
ollama serve
# Tester la connexion
curl http://localhost:11434/api/versionSolution :
- Vérifier que le répertoire workspace existe
- Vérifier les permissions sur le répertoire
- Vérifier que le filepath n'essaye pas de faire une traversée (
../../../etc/passwd= bloqué)
# Lancer les tests avec output verbose
go test -v ./core/bus
# Ajouter des logs
go test -run TestPublishReceived -v ./core/bus# Windows
GOOS=windows GOARCH=amd64 go build -o axiom.exe -ldflags="-s -w" ./
# macOS
GOOS=darwin GOARCH=arm64 go build -o axiom-darwin -ldflags="-s -w" ./
# Linux
GOOS=linux GOARCH=amd64 go build -o axiom-linux -ldflags="-s -w" ./- Documentation Axiom : GitHub
- Event Bus :
core/bus/eventbus.go - Sovereign API :
core/security/clearance.go - Module Contrat :
core/module/module.go - Wails v2 : wails.io
- Ollama : ollama.ai
- Mistral API : mistral.ai
Axiom est sous licence MIT. Libre d'usage commerciale et personnelle.
Axiom v0.2.0-alpha — Modular Software Engineering Platform
Maintenance: Romain Laffont & Axiom Core Team