Execution Layer for Odoo
Governance.
How the Execution Layer enforces your rules on the server, where a prompt cannot reach: per-user OAuth identity, access bundles and presets, the intersection-only permission model, hidden and required fields, preconditions, and export controls.
Prompt instructions are suggestions. The Execution Layer turns your operating rules into server-side enforcement that holds no matter what the model was told, or by whom.
Identity: every user is themselves
Each user connects under their own Odoo identity. The suite ships a built-in OAuth 2.1
authorization server (PKCE S256, dynamic client registration, discovery, refresh rotation,
revocation), because Odoo ships no OAuth provider of its own. For unattended jobs there are
scoped API keys (nmk_...), each with per-operation scopes, an optional expiry, an IP
allowlist, a rate limit, and one-click revocation.
There is no service account. The AI touches exactly what that person may touch, under Odoo’s own access rights and record rules, and the audit says who.
Connections
A connection is the persistent grant behind rotating tokens: one AI client, connected as one user. At consent, the user picks an access level (read, write meaning create and update, or full) and, optionally, a subset of models. The connections list shows every client, its Odoo user, its chosen permission, its last use and IP, its calls today, and a revoke control.
Access bundles
A bundle is a plain-language role that decides, per user: which models and fields are in scope, which domains apply, which tools even exist, whether export is allowed, and the maximum records per read. Shipped presets include the plain Odoo permissions: read only / read and write / full bundles.
A tool the AI cannot see is a tool it cannot misuse. Tools outside a bundle are absent from the tool list, not present-but-denied, so the model never learns they exist.
The permission model: intersection only
Effective permission is an intersection that can only ever narrow:
Odoo ACL ∩ the user's bundle ∩ this connection's level and models ∩ the token's scopeNothing in the chain can widen what Odoo already allows. A read-only bundle is structurally unable to reach configuration; a narrow connection cannot be talked into a broad one.
Hidden and required fields
- Hidden fields are simply absent from
describe_model, so the AI does not see them. - Fields can be flagged read-only for MCP even where Odoo would allow a write.
- Required-for-AI fields are enforced more strictly than Odoo’s own: a write missing one returns
FIELD_REQUIRED. For example, “a task created or updated by AI must always have assignees”.
Preconditions
Preconditions are your business rules, evaluated on the server before any action. They are declarative (a field must be set, a field must be one of a set, a related record must exist or be active, two fields must compare a certain way) or, where you need it, an expert Python expression. Each renders as a sentence with a fix location, so a refusal tells the agent, and the person, exactly what to do:
"A sales order needs a customer reference before it is confirmed.""An invoice needs a due date before it is posted."A precondition that fails returns PRECONDITION_FAILED, never a silent skip.
Export controls
Export can be disabled per bundle, and every export is audited with its format, the fields, the row count, the totals, whether it was truncated, and the user.
Dry-run with the simulator
The Execution Layer includes a simulator: run any tool as any user and see whether it would be allowed, denied, or sent for approval, and the evidence row it would write, without executing or recording anything. It is the fastest way to check a bundle or a precondition before you rely on it.
Destructive actions get their own gate. Next: approvals.