Open-source AI agent for Microsoft 365.
Project description
███████╗██████╗ ███████╗ █████╗ ███╗ ██╗
██╔════╝██╔══██╗██╔════╝██╔══██╗████╗ ██║
█████╗ ██████╔╝███████╗███████║██╔██╗ ██║
██╔══╝ ██╔══██╗╚════██║██╔══██║██║╚██╗██║
███████╗██║ ██║███████║██║ ██║██║ ╚████║
╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝
Your office. Your rules. Your AI. ...and you, amplified.
Open-source AI agent for Microsoft 365. Apache 2.0. Runs on your laptop against your own tenant. No data leaves unless you tell it to.
Why ersan
If you use Microsoft 365 and want an AI agent that:
- doesn't ship your inbox to a third-party SaaS,
- gates every action through a policy you can read,
- redacts PII and credentials from outbound text,
- ships a working CLI in seconds via
pip install, - is bilingual out of the box (English + Turkish); community translations welcome — see docs/i18n.md,
then ersan is for you.
Architecture
ersan is the gate (owns the user relationship), not infrastructure (consumed by other agents). Capabilities load as skills, packaged into plugins.
Three pillars:
- Core: pydantic-ai Agent runtime, CLI, REPL
- Policy: trust tiers (
ask_first,do_and_tell,do_it) gating every tool call - Shield: PII / credential redaction on outbound text + ReDoS-safe regex compilation, audit-log SHA-256 hash chain, and per-run policy isolation (defense-in-depth landed in v0.5.0; see SECURITY.md)
In the current runtime, ersan's core is pydantic_ai.Agent. Tool selection
is LLM-driven from natural-language prompts, ersan.policy enforces
tier-based gating via PolicyCapability.wrap_tool_execute(), and
ersan.shield enforces pattern-based blocking plus redaction via
ShieldHook registered on the tool:pre event of
ersan.hooks.HookRegistry, fired by HookBridgeCapability.
The ersan runtime ships no bundled M365 toolset yet — Microsoft Graph OAuth via MSAL and the Outlook inbox skills are scheduled for a future release (US-007). Today the CLI exposes the bare Agent runtime: bring your own toolset (see docs/skills.md) or wait for the bundled Inbox MVP.
Privacy
- No telemetry. Zero phone-home.
- No third-party LLM calls unless your config calls them. Structurally
enforced — local-provider client construction is covered by
tests/integration/test_local_provider_no_network.py, which proves the provider factories make zero outbound requests during construction. - LLM credentials stay local. API keys live in
ERSAN_LLM_API_KEY(your env), never written to disk by ersan, never logged, redacted as***inersan configoutput and JSON repr (viapydantic.SecretStr). M365 OAuth tokens are NOT yet stored — the MSAL device-code flow lands with US-007. - Audit-log to your own sink (
~/.ersan/audit.log). Every entry carries a SHA-256prev_hash(v0.5.0) so tampering is detectable viaersan.policy.audit.verify_audit_chain.
Platform support
ersan is platform-agnostic by construction (per Constitution §Cross-platform invariants). Every PR runs against:
- Per PR (6 jobs):
ubuntu-latest× Python 3.10 / 3.11 / 3.12 / 3.13;windows-latest× Python 3.13;macos-latest× Python 3.13. - Weekly cron (12 jobs): full cartesian — every supported OS × every supported Python version.
- On every release tag (6 jobs): smoke-install matrix —
{ubuntu, windows, macos} × {Python 3.10, 3.13}runspip installagainst the freshly built wheel before it reaches PyPI via Trusted Publishing with Sigstore PEP 740 attestations.
A wheel does not reach PyPI until the smoke-install matrix passes on all three operating systems.
Roadmap
| Version | Status | Shipped |
|---|---|---|
| v0.1.x | ✅ released | Architecture-complete: foundations + eval harness + skill loader |
| v0.2.x | ✅ released | Model Provider Abstraction — ersan.providers, local-first Ollama config, Anthropic config, privacy-invariant provider tests |
| v0.3.0 | ✅ released | Constitutional pillars — bundled slim port of ersan.policy + ersan.shield, wired into the loader with the default retail ask_first policy and ~/.ersan/audit.log audit path |
| v0.4.0 | ✅ released | Agent runtime adoption — pydantic_ai.Agent, PolicyCapability, HookBridgeCapability, slim REPL + one-shot CLI (US-006) |
| v0.5.0 | ✅ released | Env-first config (pydantic-settings + Catwalk-style provider registry, US-010) + defense-in-depth security primitives (5 review findings, see SECURITY.md) |
| v0.6.0 | ✅ released | Bilingual i18n out of the box — ersan.i18n with English + Turkish locales, ERSAN_LANG env var, packaged via importlib.resources (US-008). Adds structlog as a runtime dep (first adopter; repo-wide migration tracked as US-011) |
| Version | Status | Goal |
|---|---|---|
| v0.x — next | planned | Inbox MVP — Microsoft Graph OAuth via MSAL device-code flow, ~/.ersan/tokens.json storage, first bundled Outlook toolset (US-007) |
| v0.x — later | planned | OpenTelemetry runtime instrumentation (US-009); repo-wide structlog adoption (US-011); OPA-policy evaluation (US-012) |
Configuration
ersan's runtime config is env-first — the source of truth is
ersan.settings.LLMSettings. Every config field maps to one
ERSAN_LLM_* env var.
Local Ollama example:
export ERSAN_LLM_PROVIDER=ollama
export ERSAN_LLM_MODEL=llama3
export ERSAN_LLM_BASE_URL=http://localhost:11434/v1
Anthropic example:
export ERSAN_LLM_PROVIDER=anthropic
export ERSAN_LLM_MODEL=claude-3-5-sonnet-latest
export ERSAN_LLM_API_KEY=$ANTHROPIC_API_KEY
Run ersan config to see what the runtime resolved from your env (the
API key is masked as "***"). Run ersan config --schema to print the
JSON Schema. See docs/configuration.md for the
full env surface.
How ersan is built
ersan is maintained by Ersan Bilik with AI coding assistants coordinated through GitHub Spec-Kit and a model-pinned multi-agent dev cycle (D-019). Every release ships through the same flow: spec → plan → tasks → implement → 2-way red-team review → fix → merge → architect-owned sole-truth gate → release.
Get involved
- 🐛 Issues
- 📜 Constitution — what ersan is and isn't
- 🤝 Contributing
- 🔒 Security disclosure
- 📜 Code of Conduct
License
Apache 2.0. See LICENSE.
Built by Ersan Bilik.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ersan-0.6.0.tar.gz.
File metadata
- Download URL: ersan-0.6.0.tar.gz
- Upload date:
- Size: 87.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e0f28fe835c23bd7a75f6f27045ec67dc2db73fc0bf9d2d731d9403af0b4aa5
|
|
| MD5 |
432269816d9e62d6c29a4fe0a630d599
|
|
| BLAKE2b-256 |
bea7f9932e437d0a6565ed7639a09c5c2a6c99597e7b7ec95653e505cb0a4e8a
|
Provenance
The following attestation bundles were made for ersan-0.6.0.tar.gz:
Publisher:
release.yml on ersan-ai/ersan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ersan-0.6.0.tar.gz -
Subject digest:
1e0f28fe835c23bd7a75f6f27045ec67dc2db73fc0bf9d2d731d9403af0b4aa5 - Sigstore transparency entry: 1356786404
- Sigstore integration time:
-
Permalink:
ersan-ai/ersan@065f3721b01af279fd389352612d9bc45f087389 -
Branch / Tag:
refs/tags/ersan-v0.6.0 - Owner: https://github.com/ersan-ai
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@065f3721b01af279fd389352612d9bc45f087389 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ersan-0.6.0-py3-none-any.whl.
File metadata
- Download URL: ersan-0.6.0-py3-none-any.whl
- Upload date:
- Size: 64.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ec9d1a26fca1bd3602938cdbd8ae4d62ea9a269e12b3153aeb26625be4ae3fb
|
|
| MD5 |
123525df8d32b70faf2cdf06d33ec6eb
|
|
| BLAKE2b-256 |
bf729ab6e9bbc661f0b1e1bce9584d18aef132a27f23e1ff7d3f5de5f4165f33
|
Provenance
The following attestation bundles were made for ersan-0.6.0-py3-none-any.whl:
Publisher:
release.yml on ersan-ai/ersan
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ersan-0.6.0-py3-none-any.whl -
Subject digest:
1ec9d1a26fca1bd3602938cdbd8ae4d62ea9a269e12b3153aeb26625be4ae3fb - Sigstore transparency entry: 1356786427
- Sigstore integration time:
-
Permalink:
ersan-ai/ersan@065f3721b01af279fd389352612d9bc45f087389 -
Branch / Tag:
refs/tags/ersan-v0.6.0 - Owner: https://github.com/ersan-ai
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@065f3721b01af279fd389352612d9bc45f087389 -
Trigger Event:
push
-
Statement type: