sync_base — Core

Version: 19.0.0.4.0 · Depends: base_automation · License: OPL-1

sync_base is the foundation of the Odoo Sync framework. It provides the data models, mapping engine, and orchestration logic used by all transport and application modules.

See also

sync_base — full SyncBaseController reference.


Base Models

class ip.swarm[source]

Sync Swarm / Network.

Represents a distinct Sync network (e.g., ‘Public Global’, ‘Internal Enterprise’). A Python Gateway daemon syncs with Odoo on behalf of an entire Swarm, managing the state of all Nodes within that Swarm.

This is the unified orchestrator for all Gateway synchronization and network state.

See also

Swarms — Swarm user guide.

Attributes

name: fields.Char[source]
Swarm name or alias, for display purposes.
network_type: fields.Selection[source]
Transport protocol used by all nodes in this swarm. Extended by sync_kubo / sync_http.
gateway_id: fields.Many2one[source]
The local gateway node managing this swarm.
node_ids: fields.One2many[source]
The nodes participating in this swarm.
topic_count: fields.Integer[source]
Number of active topics in this swarm.

Inbound Admission

_admit_payload(payloads: list[dict], from_node) → list[dict][source]

Validate and normalise a batch of inbound payloads before they enter the ledger.

Mirrors the gateway UnifiedBouncer checks:

  • Drops all payloads from blocked nodes (trust_level is “blocked”)

  • Drops payloads missing topic_name or data_json

  • Drops spoofed payloads (from_peer != from_node.peer_char for non-gateways)

  • Sets direction=’inbound’ on each admitted payload

Parameters:
  • payloads – Raw payload dicts from the network

  • from_node – Authenticated ip.node that submitted the payloads

Returns:

Normalised payloads ready for _sync_push

Outbound Dispatch

_dispatch_events(events)[source]

Deliver events (outbound ip.event recordset) over this swarm’s transport.

Override to implement actual delivery. ‘offline’ is a no-op transport — events are marked ‘sent’ without network delivery.

CID Fetching

_express_fetch_cids(cids)[source]

Schedule a post-commit express fetch for cids (ip.cid recordset).

Deferred to post-commit so CID records are visible to the new cursor opened by the background thread or the external gateway call. Override _do_express_fetch_cids in transport modules.

_do_express_fetch_cids(cids)[source]

Transport-specific CID fetch. Called post-commit by _express_fetch_cids.

Sync Helpers

_compute_sync_time() → datetime[source]

Compute the safe server time for synchronization.

Uses the oldest active transaction time in the database to ensure consistency, falling back to the current time if no other transactions are active.

Returns:

Safe server time as a naive datetime object.


class ip.node[source]

Network Node — transport-agnostic base.

Holds identity, swarm membership, and liveness state. Transport-specific capabilities (Kubo RPC, native HTTP) are contributed by sync_kubo / sync_http.

See also

Nodes — Node user guide.

Attributes

peer_id: fields.Many2one[source]

Inherited ip.peer identity.

swarm_id: fields.Many2one[source]
The network this node belongs to.
is_gateway: fields.Boolean[source]
Enable to flag this node as a local gateway managed from this Odoo instance.
api_user_id: fields.Many2one[source]
Odoo user whose credentials authenticate this node for remote API access.
last_seen: fields.Datetime[source]

Updated by heartbeat. Used to compute is_online.

is_online: fields.Boolean[source]
Gives an indication of how recently this node was last seen. Do not use in domains.

Transport Interface

_check_callable()[source]

Check permissions and swarm assignment before any transport call.

add(payload)[source]

Upload content; return the ip.cid record (state=pinned). Override per transport.

cat(cid: str | models.BaseModel) → bytes | None[source]

Fetch content by CID; return raw bytes. Override per transport.

Parameters:

cid – CID string or ip.cid record.

Returns:

Raw bytes of the content, or None if not found / unreachable.

Raises:

NotImplementedError – when no transport module is installed.

action_fetch_identity()[source]

Probe all nodes in self and return a notification summarising reachability.

_fetch_identity()[source]

Test connectivity and return the subset of nodes successfully reached.

Transport modules override this to probe their network interface. The base implementation returns an empty recordset (no transport = no reachable nodes).

CRUD

create(vals_list)[source]

Resolve or create the underlying ip.peer record and promote gateways to ‘trusted’.

write(vals)[source]

Track liveness transitions and auto-promote gateways to ‘trusted’.

Presence & Heartbeat

_get_online_domain()[source]

Return the domain that matches nodes that are currently online.

_on_online_change()[source]

Notify connected clients that node liveness changed so the kanban refreshes live.

_heartbeat_sweep()[source]

Post-import hook: stamp last_seen and flush any pending direct outbox events.

Called by the heartbeat topic’s import server action with the matched ip.node records. Only invoked for foreign nodes whose heartbeat payloads were just ingested.

_heartbeat_reap()[source]

Post-export hook: mark nodes offline when last_seen is stale.


class ip.peer[source]

Sync Cryptographic Identity.

Represents the public/private keys of anyone on the network. Records here are created passively whenever a new signature is detected. A Peer does NOT imply a direct, persistent network connection.

See also

Peers — Peer user guide.

Attributes

peer_char: fields.Char[source]
Base58btc-encoded PeerID (libp2p format). Unique.
public_key: fields.Text[source]
PEM-encoded Ed25519 public key, used to verify signatures from this peer.
private_key: fields.Text[source]
PEM-encoded Ed25519 private key, used to sign data authored by this peer.
trust_level: fields.Selection[source]
The trust level determines if the Sync daemon fetches the cid content:
- Unknown: attempt fetching, create the cid if content found
- Trusted: attempt fetching, create the cid in any case
- Blocked: no fetching, cid ignored
can_author: fields.Boolean[source]
True when a private key is present, False otherwise.

Cryptography

action_generate_keys()[source]

Generate or extract Ed25519 keys for this peer.

When peer_char is already a valid Ed25519 PeerID (identity-multihash), the public key is extracted directly from it and peer_char is unchanged. The private key cannot be derived and is left empty.

When peer_char is absent or a placeholder, a fresh keypair is generated and peer_char is derived from it.

action_validate_keys()[source]

Validate keys and PeerID for this peer.

Returns a client notification action summarizing results.

_peer_char_from_pubkey(pub_key)[source]

Derive the Sync PeerID from an Ed25519 public key object.

Format: Base58btc( identity-multihash( libp2p-protobuf( pubkey ) ) ) Produces the canonical ‘12D3KooW…’ PeerID used by libp2p / Sync.

_sign_payload(payload, encoding='base64')[source]

Sign a payload using the peer’s private key.

Parameters:
  • payload – The data to sign (str or bytes).

  • encoding – Format of the returned signature (‘base64’, ‘hex’, or ‘raw’).

Returns:

The signature in the requested encoding.

_verify_signature(payload, signature, encoding='base64')[source]

Verify a signature against a payload using the peer’s public key.

Parameters:
  • payload – The original signed data (str or bytes).

  • signature – The signature to verify (str or bytes).

  • encoding – The encoding of the provided signature (‘base64’, ‘hex’, or ‘raw’).

Returns:

True if mathematically valid, False otherwise.


class ip.topic[source]

Sync PubSub Topic — API Router and Configuration Engine.

A Topic is a communication channel. It defines: - Schema: What the data looks like. - Import: How and when ip.event inbox payloads are converted into (or update) native Odoo records. - Export Generation: How and when native Odoo records are converted into ip.event outbox payloads. - Export Delivery: How the Gateway retrieves those payloads (Push Webhook vs. Pull Polling).

See also

Configuration — configuration matrix and delivery modes. Events — event lifecycle, routing and mapping states.

Identity

name: fields.Char[source]
Sync PubSub topic name (e.g., swarm/core/partner) | libp2p protocol
schema_id: fields.Many2one[source]
The data structure required to communicate on this topic
routing_mode: fields.Selection[source]
How the topic routes events:
- Broadcast (all): multi-node broadcast to all peers in active swarms with at-most-once delivery.
- Direct (p2p): targeted point-to-point delivery to specific nodes resolved by __nodes__ in payload.
swarm_ids: fields.Many2many[source]
Swarms this topic is allowed to broadcast on. Events are fanned out once per swarm; direct events are filtered to nodes inside these swarms.

Import

auto_import: fields.Boolean[source]
Automatically map inbound events to records.
import_domain: fields.Char[source]
Events matching this domain are candidates for auto-import to map them into records.
import_mode: fields.Selection[source]
Instant: Maps inbound events immediately in ip.event.create() when domains match.
Batch: Events are parked as pending and mapped by the import cron.
import_batch_size: fields.Integer[source]
Maximum number of events to ingest per cron execution (0 = no limit).
import_server_action_id: fields.Many2one[source]
Post-import server action, called with the new odoo records (if any).

Export

export_mode: fields.Selection[source]
Instant: Creates an event instantly when a record is saved.
Batch: A cron job sweeps records periodically to generate events.
export_domain: fields.Char[source]
Defines which Odoo records are candidates for auto-export.
- Batch: The export cron sweeps records matching this domain.
- Instant: The automation synchronously triggers for records matching this domain.
Once an event is created, this domain won’t restrict it from being sent.
event_array_size: fields.Integer[source]
Maximum number of Odoo records per ip.event of type ‘array’.
export_batch_size: fields.Integer[source]
Maximum number of outbound ip.event payloads to generate per export run (0 = no limit).
If payload type is ‘Array’, the export will sweep (Batch Size x Array Size) Odoo records per run.
export_server_action_id: fields.Many2one[source]
Post-export server action, called with the newly exported odoo records (if any).
export_context_action_id: fields.Many2one[source]
Server action bound to the model’s Action menu for manual record-level export.

Delivery

auto_deliver: fields.Boolean[source]
If enabled, events generated by this topic are automatically queued for delivery. If disabled, events remain ‘pending’ until you manually click ‘Run Delivery’ (push).
deliver_mode: fields.Selection[source]
Determines how pending events reach the Gateway.
PUSH: Odoo actively sends events to the Gateway.
- Auto-Deliver ON: Dispatched instantly. Failures are retried by the Global Postman.
- Auto-Deliver OFF: Requires manual dispatch. Failures are NOT retried.
PULL: The Gateway continuously polls Odoo for events.
- Auto-Deliver ON: The Gateway is permitted to fetch pending events.
- Auto-Deliver OFF: The Gateway is blocked. You must manually force a push.
deliver_retries: fields.Boolean[source]
If disabled, events are only sent once.
- Push: The Global Postman will not attempt to resend if the Gateway is offline.
- Pull: Odoo will not re-serve the events if the Gateway crashes before confirming them.
Ideal for high-volume transient data.
deliver_batch_size: fields.Integer[source]
Maximum number of events to dispatch per cron run (0 = no limit). Actual batch may be smaller when concurrent deliveries lock events for sending.

Garbage Collection

auto_delete: fields.Boolean[source]
Automatically delete old events in this topic matching a specific domain.
delete_domain: fields.Char[source]
Events in this topic matching this domain will be unlinked by the Garbage Collection cron.
Context variables: datetime, timedelta, relativedelta, context_today, env.

Infrastructure

_sync_infra()[source]

Synchronize the topic automation infrastructure (base.automation and ir.cron).

Import

action_run_import()[source]

Manually run the Import pipeline (single batch).

Sweeps pending inbound events for this topic and maps them into Odoo records. Bypasses cron limits to process up to the import_batch_size. Does NOT re-trigger the import cron.

Returns:

A UI notification action.

_action_run_import(events=None, cron_mode=True)[source]

Map inbound events to records.

Parameters:
  • events – Explicit events to map. If None, sweeps pending events (limit=import_batch_size).

  • cron_mode – If True, re-triggers the import cron if the limit is hit.

Returns:

Tuple (processed_events, mapped_odoo_records).

Export + Delivery

action_run_export_deliver()[source]

Manually run the Export and Delivery pipelines (single batch).

  1. Sweeps records matching the export_domain to generate pending events (up to the export_batch_size).

  2. Sweeps this topic’s outbox (newly generated + previously pending/stalled events) and attempts to push them to the network (up to the deliver_batch_size).

Bypasses automation guards (auto_export, auto_deliver, and pull mode restrictions). Does NOT re-trigger any background crons.

Returns:

A UI notification action.

_action_run_export_deliver(records=None, cron_mode=True)[source]

Run the complete outbound pipeline: event generation and delivery.

Parameters:
  • records – Explicit Odoo records to export. If None, sweeps records matching export_domain up to export_batch_size.

  • cron_mode – If True, performs extra safety checks and may re-trigger the export cron or global delivery cron if limits are hit.

Returns:

Tuple of (exported_odoo_records, dispatched_ip_events).

Export Only

action_run_export()[source]

Manually run the Export pipeline only (no delivery, single batch).

Sweeps records matching the export_domain and generates ip.event records. The events are parked in ‘pending’ and are NOT pushed to the network. Does NOT re-trigger the export cron.

Returns:

A UI notification action.

Delivery Only

action_run_deliver()[source]

Manually run the Delivery pipeline only (single batch).

Sweeps all pending or stalled events currently sitting in this topic’s outbox and attempts to push them to the Gateway. Bypasses the ‘pull’ restriction, allowing admins to force-push events that are normally polled. Does NOT re-trigger the global postman cron.

Returns:

A UI notification action.

Garbage Collection

action_run_delete()[source]

Manually run the Garbage Collection pipeline (single batch).

Evaluates the delete_domain strictly against events belonging to this topic and permanently unlinks them from the database to reclaim space. Does NOT re-trigger the global collector cron.

Returns:

A UI notification action.


class ip.data[source]

Sync Data Mixin (Abstract).

This is not a physical database table. It is a “Mixin” that injects standardized JSON storage, byte-size computation, indexing and search capabilities directly into any model that inherits it (like ip.event and ip.cid).

Attributes

data_json: fields.Json[source]
Contains a parsed Python dictionary
json_size: fields.Integer[source]
Physical UTF-8 byte size of the parsed JSON payload.
netw_size: fields.Integer[source]
Total byte size as reported by the transport layer (e.g. DAG overhead for Kubo, raw bytes for HTTP).
mapping_info: fields.Char[source]
Records why a mapping process ignored or failed this payload.

JSON search

init()[source]

Add a GIN index for json search on the concrete tables (ip_cid, ip_event).

Intercept DB searches to natively compile JSON paths into AST nodes.

filtered_domain(domain)[source]

Intercept memory filtering to natively compile JSON paths into AST nodes.

Side effect:

writes any type mismatch error to mapping_info

_compile_json_domain(domain)[source]

Translate dotted JSON paths (data_json.x.y) into native Odoo 19 AST nodes. Includes full support for String matching, IN clauses, and Numeric casting.


class ip.event[source]

Sync Network Event (Gossipsub Envelope).

Represents a single, cryptographically signed message received over the P2P network. It contains the payload (inherited from ip.data), who sent it, and the cryptographic signature.

Events are local to this database and are immutable once created.

See also

Events — event lifecycle, routing and mapping states.

Attributes

from_peer: fields.Char[source]
The sender’s network node PeerID (multibase string).
from_node_id: fields.Many2one[source]
The sender’s network node (local gateway, foreign host).
topic_id: fields.Many2one[source]
The PubSub topic (ip.topic) this event belongs to.
topic_name: fields.Char[source]
Denormalized PubSub topic path string (e.g. /odoo/myswarm/v1/…).

Computed from ip.event.topic_id.

seqno: fields.Char[source]
Sequence number (UUID by default). Unique by origin (deduplication).
signature: fields.Char[source]
Standard Base64 encoded string (no padding). Created by the sender’s Ed25519 private key signing the raw UTF-8 payload.
direction: fields.Selection[source]
Either outbound (queued for delivery) or inbound (pending import).
to_peer: fields.Char[source]
The recipient’s network node PeerID (multibase string).
to_node_id: fields.Many2one[source]
The recipient’s network node (local gateway, foreign host).
routing_mode: fields.Selection[source]
How the event is routed:
- Broadcast (all): multi-node broadcast to all peers in active swarms with at-most-once delivery.
- Direct (p2p): targeted point-to-point delivery to a specific recipient node.
routing_state: fields.Selection[source]
Network lifecycle:
- Pending: in outbox, ready to send
- Sending: in transit, handed to gateway
- Sent: sent over network by gateway
- Failed: permanent delivery failure, not retried
mapping_state: fields.Selection[source]
Mapping lifecycle:
- Ignored: not scheduled for mapping
- Waiting: waiting for CID to be fetched
- Pending: ready to be mapped
- Invalid: invalid json according to schema
- Failed: raised an error during mapping
- Mapped: successfully used
cid_ids: fields.Many2many[source]
CIDs referenced by the payload.

Network Sync

_sync_push(vals_list)[source]

Push using (from_peer, seqno) as the sync key. Ensure CIDs exist before mapping.

_sync_pull(last_sync, pending=True, topics=None, nodes=None, limit=200, **kwargs)[source]

Pull outbound pull-mode events for gateway polling.

Parameters:
  • last_sync – Only include events with write_date ≥ this timestamp. If False, skips the watermark filter.

  • pending – Determines the delivery states and locking mechanism. If True, returns pending and stalled sending events being _locked_with_state(). If False, returns events in all states does not lock them for sending.

  • topics – Recordset or domain to scope event topics. If None, defaults to all pull-mode, auto-deliver topics.

  • nodes – Recordset or domain used to scope direct events. If None, skips node filtering (all nodes).

  • limit – Maximum number of events to return (0 = no limit; default 200).

_sync_pull_self() → list[dict][source]

Return payload dicts for this event recordset. Override to add transport-specific fields.

Delivery

_get_dispatch_domain(pending=True, topics=None, nodes=None)[source]

Get the domain for fetching outbound events.

Parameters:
  • pending – only include pending and stalled sending events.

  • topics – the specific topics to use (recordset or domain).

  • nodes –

    specific nodes to target (recordset or domain). When provided:

    • direct events are scoped to matching nodes.

    • broadcast events are always included.

_locked_with_state(state)[source]

Safely set routing_state for events in self via SKIP LOCKED.

Events already locked by another transaction are silently skipped (fire-and-forget semantics). Does NOT update write_date, preserving the original write_date for watermark-based polling.

Parameters:

state – Target routing_state (‘sending’, ‘sent’, or ‘failed’).

Returns:

Recordset of events that were actually updated.

_try_sending()[source]

Dispatch outbound events grouped by swarm.

Determines the swarm from the target node (direct) or source node (broadcast), then calls swarm._dispatch_events so sync_base stays transport-agnostic.

Events with no resolvable ref_node are permanently failed rather than silently dispatched via the offline transport (which would mark them ‘sent’ with no delivery).

Mapping

_try_mapping(auto_retry=True)[source]

Attempt to run the AST mapping and return mapped Odoo records.

CRUD

create_from_records(records, topic)[source]

Create outbound network events from Odoo business records.

Parameters:
  • records – The Odoo records to export (singleton or recordset).

  • topic –

    The ip.topic that defines schema, routing and swarm targets.

    • Swarms: topic.swarm_ids or the swarm in context key sync_target_swarm_ids.

    • Direct: one event per valid non-gateway target node whose swarm is in swarms.

    • Broadcast: one event per swarm.

Returns:

Recordset of the newly created outbound ip.event records.

Raises:

UserError – If the topic has no schema, or if no target nodes exist for a direct-routing topic.

create(vals_list)[source]

Classify inbound/outbound state, manage instant import/export.

For all events:

  • Backfills host_node_id on any CIDs linked to inbound events that have no host set.

For inbound events:

  • Marks topics that are inactive, schema-less or manual as ignored.

  • Marks events that reference any non-pinned CIDs as waiting.

  • Evaluates the topic import domain and marks matching events as pending; if the topic’s import_mode is instant, the import action is executed immediately for those events.

  • Marks remaining inbound events as ignored.

For outbound events:

  • If the topic has auto_export and auto_deliver and its export_mode is instant and deliver_mode is push, the topic delivery action is executed immediately for those events.

write(vals)[source]

Protect cryptographic network fields from being altered after creation.

The following fields are protected from modification after creation:

  • Always protected: data_json (raw payload).

  • Inbound only: from_peer, seqno and signature (envelope fields).

Raises:

UserError – If an attempt is made to modify protected fields.

Public Actions

action_run()[source]

Map inbound (ingest) and send outbound (deliver) events. Always routed through the topic’s import/delivery actions.


class ip.cid[source]

Sync Content Identifier (CID).

Represents a physical file or immutable data blob stored on the Sync network. Unlike Events (which are transient messages), a CID represents global, decentralized state. Multiple events might reference the exact same CID.

CIDs are immutable once created.

See also

CIDs — content addressing and fetch states.

Attributes

cid: fields.Char[source]
Sync Content Identifier (CID) string
cid_state: fields.Selection[source]
State of the CID in the Sync network
data_raw: fields.Binary[source]
Raw binary content of this CID, stored via ir.attachment in Odoo’s filestore. Odoo automatically detects duplicate filestore content and stores it only once.
file_size: fields.Integer[source]
Byte size of the stored raw binary content.
host_node_id: fields.Many2one[source]
The node that first broadcasted this CID. Used by HTTP transport to locate the file.
event_ids: fields.Many2many[source]
Events that reference this CID.

Inverse of ip.event.cid_ids.

CRUD

find_or_create(cid_str: str, defaults: dict)[source]

Atomically find or create an ip.cid record for cid_str.

Uses INSERT ON CONFLICT DO NOTHING to resolve the existence check in a single round-trip, eliminating the search-then-create race window. Defaults are applied via an ORM write on newly inserted rows so computes and hooks fire correctly.

write(vals)[source]

Protect immutable fields and, when a CID is pinned, resume import of waiting events.

Actions

_action_fetch_content()[source]

Fetch, store, and re-anchor this CID to the local gateway.

Reassigns host_node_id to the swarm gateway after storing (if fetched). :returns: True if content was fetched successfully, False otherwise.

action_fetch_content()[source]

class ip.schema[source]

Bidirectional JSON ↔ ORM translation engine.

Holds a data_schema (JSON Schema draft-07) for wire-format validation and an odoo_schema AST that maps Odoo fields to JSON paths with support for upserts, relational traversal, and binary blobs via the is_cid trait.

See also

Data Schema — JSON Schema wire-format validation. Odoo Schema — AST mapping operators and traits.

Attributes

data_schema: fields.Json[source]
JSON Schema draft-07 validation definition
odoo_schema: fields.Json[source]
AST mapping Odoo fields to/from JSON.

Static Analysis

_check_schemas()[source]

Statically analyze the AST for valid Odoo paths, syntax, and JSON schema consistency.

Outbound engine

_to_payload(records: models.Model, swarm=None) → tuple[dict | list, dict][source]

Translate Odoo records into a validated JSON payload.

If the schema root type is ‘object’, enforces a singleton record. If the schema root type is ‘array’, processes a recordset batch.

Returns a 2-tuple of (payload, trait_context) where trait_context holds:

  • __nodes__: ip.node recordset of routing targets

  • __cids__: ip.cid recordset of CIDs produced during export

_to_payload_single(record: models.Model, ast: dict, trait_hooks: dict, trait_context: dict) → tuple[dict, models.Model][source]

Translate a singleton Odoo record into a JSON payload via the AST.

Parameters:
  • record – The native Odoo recordset (must be a singleton).

  • ast – The compiled Odoo Schema Abstract Syntax Tree.

  • trait_hooks – Dict mapping trait names to their lifecycle hook callables.

  • trait_context – Shared state dictionary for tracking side-effects.

Returns:

A tuple containing the network-ready payload dictionary and an ip.node recordset of routing targets (if __nodes__ is defined).

Raises:

ValidationError – If the generated payload violates the data_schema.

_export_relation_dict

Inbound engine

_to_records(payload: dict | list, event=None) → models.Model[source]

Translate JSON payload into Odoo records using a 3-Pass DataLoader architecture.

Parameters:
  • payload – A network-ready JSON object or array to be mapped.

  • event – The originating ip.event record (provides context for traits, e.g. is_cid).

Returns:

A recordset of the successfully created or updated Odoo records.

The trait_context exposed to trait hooks during inbound mapping will include: - payload_list: The entire normalized list of payloads being processed in this batch.

_pass1_discover_lookups(payloads: list[dict], ast: dict, trait_context: dict | None = None) → dict[tuple, set][source]

Scan all payloads to collect unique relational lookup values defined in the AST.

Parameters:
  • payloads – The list of JSON dictionaries being mapped.

  • ast – The compiled Odoo Schema Abstract Syntax Tree.

  • trait_context – The shared context dict for trait side-effects.

Returns:

A dict mapping lookup keys to sets of value tuples. Format: {(comodel_name, field_paths_tuple): {(val1, val2), …}}

_pass2_resolve_cache(payloads: list[dict], ast: dict, lookup_requests: dict[tuple, set]) → tuple[dict, dict, str][source]

Execute bulk ORM searches for discovered lookups to prevent N+1 queries.

Parameters:
  • payloads – The list of JSON dictionaries.

  • ast – The compiled Odoo Schema AST.

  • lookup_requests – The grouped relational targets from Pass 1.

Returns:

A tuple containing (resolution_cache, root_existing, root_on_miss):

  • resolution_cache: {(comodel_name, field_paths_tuple, value_tuple): record_id}

  • root_existing: {payload_index: target_odoo_record} for upserts

  • root_on_miss: String policy (‘create’, ‘ignore’, ‘fail’) for the root mapping.

_pass3_assemble_and_execute(payloads: list[dict], ast: dict, res_cache: dict, root_existing: dict, root_on_miss: str, trait_hooks: dict, trait_context: dict) → models.Model[source]

Assembles final dictionary values, applies traits, and executes ORM writes/creates.

Parameters:
  • payloads – The list of JSON dictionaries.

  • ast – The compiled Odoo Schema AST.

  • res_cache – The pre-populated ORM resolution cache from Pass 2.

  • root_existing – Pre-identified existing records for root upserts.

  • root_on_miss – Policy dictate for unmatched root records.

  • trait_hooks – Dict mapping trait names to their lifecycle hook callables.

  • trait_context – Mutable context dictionary for accumulating trait side-effects.

Returns:

Recordset of all created and updated models.

_assemble_inbound_vals(payload: dict, ast: dict, target_fields: dict, resolution_cache: dict, trait_hooks: dict, trait_context: dict) → tuple[dict, dict, dict, list][source]

Construct the final Odoo ORM vals dictionary for a single JSON payload.

Parameters:
  • payload – The raw JSON dictionary.

  • ast – The compiled Odoo Schema AST.

  • target_fields – The _fields dictionary of the target Odoo model.

  • resolution_cache – The DataLoader memory cache from Pass 2.

  • trait_hooks – Dict mapping trait names to their lifecycle hook callables.

  • trait_context – Shared state dictionary for tracking side-effects.

Returns:

vals dict, relational writes, and set_for cleanup tasks keyed by odoo field.

_build_relation_commands(payload: dict, comodel: str, match_fields: dict, field_cfg: dict, is_multi: bool, resolution_cache: dict, trait_hooks: dict, trait_context: dict) → tuple[list | int | None, dict, dict | None, dict | None][source]

Resolve relational lookups and format them into Odoo ORM commands. If the field is a x2many array, it returns a list of Odoo Command tuples. If it’s a many2one scalar, it returns an integer ID for an existing record.

Parameters:
  • payload – The raw JSON dictionary being processed.

  • comodel – The target Odoo model name (e.g., ‘res.partner’).

  • match_fields – Dict mapping Odoo paths to JSON paths for the lookup keys.

  • field_cfg – The AST configuration for this field containing command and on_miss.

  • is_multi – True if the target Odoo field expects an array of records.

  • resolution_cache – The pre-populated DataLoader cache from Pass 2.

  • trait_hooks – Dict mapping trait names to their lifecycle hook callables.

  • trait_context – Shared state dictionary for tracking side-effects.

Returns:

3-tuple of (relational_instruction, comodel_writes_dict, set_for_task_or_None).

Trait registry

_get_trait_registry()[source]

Return registered x- traits with pre/mid/post lifecycle hooks.

Override to register additional traits. Hook signature: (self, value, config: dict, trait_context: dict) -> value where config is {“ast_nodes”: […]} for pre/post hooks, and the full field AST dictionary for mid hooks.

_collect_ast_traits(ast: dict) → dict[source]

Recursively scan the AST to discover and load all active traits.

Parameters:

ast – The full Odoo schema AST.

Returns:

A dictionary mapping active trait names to their {“hooks”: dict, “configs”: list} where configs contains all the exact AST node dictionaries utilizing the trait.

_run_global_hooks(phase: str, trait_hooks: dict, trait_context: dict) → None[source]

Execute all ‘pre’ or ‘post’ trait hooks.

Parameters:
  • phase – The current execution phase (‘pre’ or ‘post’).

  • trait_hooks – Dict mapping trait names to their {“hooks”: dict, “configs”: list} dictionaries.

  • trait_context – Shared state dictionary for tracking side-effects.

_apply_mid_hooks(val, field_cfg: dict, trait_hooks: dict, trait_context: dict)[source]

Sequentially apply ‘mid’ trait hooks to mutate a specific extracted value.

Parameters:
  • val – The raw extracted JSON value.

  • field_cfg – The full exact AST node configuration dictionary for this field.

  • trait_hooks – Dict mapping active trait names to their {“hooks”: dict, “configs”: list}.

  • trait_context – Shared state dictionary for tracking side-effects.

Returns:

The mutated value after all applicable traits have fired.


Exceptions

exception sync_base.MissingCidError

Raised during AST mapping if a required CID is not yet fetched from the network.

See also

Odoo Schema — is_cid trait, the waiting block, and the full MissingCidError flow. CIDs — CID state machine and how the waiting event state unblocks automatically.


Extended Models

class ir.actions.server[source]

Extension of ir.actions.server that adds a sync_send state.

When state is sync_send, executing the server action exports the active_ids records through the configured ip.topic, with optional restriction to specific swarms via sync_swarm_ids.

See also

Auto-managed Infrastructure — how the topic auto-creates and self-heals server actions.

Attributes

sync_topic_id: fields.Many2one[source]

The ip.topic used to export records when this action fires.

sync_swarm_ids: fields.Many2many[source]
Restrict broadcast to specific swarms. If unset, the event is broadcast to all swarms on the topic.

Methods

_run_action_sync_send(eval_context=None)[source]

class ir.attachment[source]

Extension of ir.attachment with CID-backed content addressing.

Adds cid_id (Many2one to ip.cid) and a computed public url (/odoo_sync/blob/<cid>/<filename>). Overrides write() to keep the CID in sync when binary content changes, and copy() to reuse the existing CID pointer rather than duplicating bytes. Exposes _get_sync_cid() for transport modules to retrieve or lazily compute the CID for any attachment.

See also

CIDs — CID lifecycle and content addressing.

Attributes

cid_id: fields.Many2one[source]

Many2one to ip.cid. Set automatically when a binary attachment is uploaded via ip.node.add().

url: fields.Char[source]

Computed public URL for CID-backed attachments (/odoo_sync/blob/<cid>/<filename>). Stored and writeable so transport modules can override the endpoint.

Methods

_compute_attachment_url()[source]

Build the public URL for CID-backed attachments.

Produces web.base.url/odoo_sync/blob/<cid>/<filename> so the browser receives a meaningful filename in Content-Disposition without any server-side lookup. Override in a transport module to route to a different endpoint (e.g. a public IPFS filestore).

write(vals)[source]

Guarantee integrity: If the binary content changes, update the CID.

copy(default=None)[source]

Zero-cost duplication: just copy the CID pointer, avoid copying physical bytes.

_get_sync_cid(host_node)[source]

Return the CID string for this attachment, computing and persisting it on first call.

Parameters:

host_node – The ip.node whose swarm determines the CID version/format.

Returns:

CID string, or None if the attachment type is not supported.


class base.automation[source]

Extension of base.automation that guards topic-managed automations.

Adds sync_export_topic_ids (back-link to owning ip.topic records) and a sync_is_managed computed flag. Overrides write() to raise a UserError when attempting to manually re-enable a managed automation and to mirror filter_domain changes back to the topic’s export_domain. Overrides unlink() to clear the topic’s auto_export flag so the topic does not attempt to recreate a deleted automation.

See also

Auto-managed Infrastructure — how topics auto-create and self-heal their base.automation records.

Attributes

sync_export_topic_ids: fields.One2many[source]

Topics whose instant-export automation this record belongs to (inverse of ip.topic.export_automation_id).

sync_is_managed: fields.Boolean[source]

True when this automation is owned by at least one ip.topic. Computed from base.automation.sync_export_topic_ids.

Methods

action_view_sync_topics()[source]
write(vals)[source]

class ir.cron[source]

Extension of ir.cron that guards topic-managed scheduled actions.

Adds sync_export_topic_ids and sync_import_topic_ids (back-links to owning ip.topic records) and a sync_is_managed computed flag. Overrides write() to raise a UserError when attempting to manually re-enable a managed cron and to mirror interval changes back to the topic. Overrides unlink() to clear the topic’s auto_export / auto_import flags so the topic does not attempt to recreate a deleted cron.

See also

Auto-managed Infrastructure — how topics auto-create and self-heal their ir.cron records.

Attributes

sync_export_topic_ids: fields.One2many[source]

Topics whose batch-export or postman cron this record belongs to.

sync_import_topic_ids: fields.One2many[source]

Topics whose batch-import cron this record belongs to.

sync_is_managed: fields.Boolean[source]

True when this cron is owned by at least one ip.topic.

Methods

action_view_sync_topics()[source]
write(vals)[source]

class sync_base.base[source]

Generic sync engine injected into every Odoo model via the base abstract mixin.

Transport modules call base._sync_push_base() (batched upsert) and base._sync_pull_base() (incremental pull) on specific gateway-facing models (ip.event, ip.cid, ip.node, …). Individual models override base._sync_push() / base._sync_pull() to supply their own field list and search domain.

Methods

_sync_push(vals_list)[source]

Call _sync_push_base with the applicable attributes for this model.

_sync_push_base(vals_list, sync_keys, defaults=None, relations=None)[source]

Execute generic upsert engine.

Highly optimised batch operations to synchronize gateway payloads into Odoo.

Execution flow:

  1. Resolve Relations — scan payloads for defined FK keys, perform a single batched lookup, and swap strings for native Odoo integer IDs.

  2. Bulk Discovery — extract the primary sync key from all payloads and execute a single search() to pull all potentially matching records into memory.

  3. Diffing & Validation — for each payload, strip sync keys, pass through _sync_update_filter, and group identical updates; prepare non-matching payloads for creation.

  4. Atomic Writes — execute a single .write() per unique payload variation and a single .create() for all new records.

  5. ID Mapping — return a summary {"created": n, "updated": n, "batches": n} dict.

Parameters:
  • vals_list – List of dicts representing incoming JSON payloads.

  • sync_keys – List of string field names used as the unique composite identifier.

  • defaults – Dict of values to apply automatically to newly created records.

  • relations – FK lookup descriptors {"source_key": {"model": "x", "field": "y", "dest": "z"}}.

Returns:

Dict mapping the sync_key(s) to the native Odoo integer record ID.

_sync_update_filter(existing_rec, update_vals)[source]

Allow models to dynamically reject updates to certain fields. Hook designed to be overridden by specific models.

_sync_update_base(updates_list: list)[source]

Fast, explicit ID update engine. Prefetches the records into memory before looping to avoid N+1 queries.

_sync_pull(last_sync: datetime | str | bool)[source]

Call _sync_pull_base with the applicable attributes for this model.

_sync_pull_base(fnames: list[str], domain: list, last_sync: datetime | str | bool, limit: int | None = None) → list[dict[str, Any]][source]

Pull records from any model with optional time filtering.

Do not include both last_sync and limit if you want all records to be eventually pulled.

Parameters:
  • model – Model name to search

  • fnames – List of fields to return

  • domain – Initial search domain

  • last_sync – Optional last sync timestamp for incremental pulls

  • limit – Optional result limit

Returns:

List of record dictionaries


class res.partner[source]

Extension of res.partner that links a contact to its Sync peer identity.

Adds peer_id (Many2one to ip.peer) enforced unique, and read-only computed fields (host_node_id, host_swarm_id) that trace the peer through to its host node and swarm.

See also

Peer (ip.peer) — peer identity, trust levels, and the host/guest peer distinction.

Attributes

peer_id: fields.Many2one[source]
The cryptographic identity used to verify data sent by this contact.
host_node_id: fields.Many2one[source]
The node currently hosting this contact’s Sync peer identity.
host_swarm_id: fields.Many2one[source]
The swarm of the node hosting this contact’s Sync peer identity.

class res.users[source]

Extension of res.users that exposes Sync identity and API-node fields.

Adds a computed api_node_id (the ip.node whose bearer-token authentication uses this user’s API key) and read-only peer, node, and swarm fields mirrored from the partner contact.

See also

Security — how bearer tokens and api_node_id are used for HTTP authentication.

Attributes

api_node_ids: fields.One2many[source]

All ip.node records whose API user is this user (at most one, enforced by a unique constraint).

api_node_id: fields.Many2one[source]
Node for which this user’s credentials are used for API authentication.
own_peer_id: fields.Many2one[source]
This user’s cryptographic Sync identity, configured on their partner contact.
host_node_id: fields.Many2one[source]
The node currently hosting this user’s Sync peer identity.
host_swarm_id: fields.Many2one[source]
The swarm of the node that hosts this user’s Sync peer identity.

Methods

action_open_api_node()[source]