-
Notifications
You must be signed in to change notification settings - Fork 0
Integration principles and architecture
This page explains how an EDAMAME conditional access integration actually works, so that you can judge whether a given third-party product can be integrated at all, and what the integration will look like if it can.
Read this before writing a custom JSON configuration: that guide is the schema reference, this one is the design reasoning behind it.
Every EDAMAME integration rests on the same separation, and it is worth stating plainly because it constrains everything else:
EDAMAME decides which devices are compliant. Your provider decides what compliance grants. EDAMAME only keeps a container in sync between the two.
EDAMAME never authors your policy. It maintains the membership of something you created — an IP range list, a firewall address group, a device tag, an authorization flag — and your own rule, written in the provider's console, reads that membership and makes the access decision. If a product offers no such container that its policy engine can key on, it cannot be integrated, no matter how good its API is.
This is also why every guide warns that manual entries get removed: EDAMAME owns the container's contents, so anything you add by hand is overwritten. Keep manual exceptions in a separate container with its own rule.
A conditional access integration is event-driven. Nothing polls, and there is no reconciliation loop.
- A device reports its posture to EDAMAME and its compliance state changes.
- That change is written to the access control table, which emits a change event.
- The event wakes the trigger engine, one device change at a time.
- The engine loads the provider's JSON configuration. For the providers listed on
the Home page this is fetched from the public
integrationsrepository; for a custom integration it is the JSON you supplied in EDAMAME Hub. - The engine fills in the configuration's placeholders — your stored secrets, an authentication token if the configuration asks for one, and the device or whole-list values described below.
- The engine executes the resulting calls against the provider's API.
Two consequences follow from step 3. First, latency is bounded by how quickly the device reports, not by a polling interval. Second, a call that fails is retried a small number of times and then abandoned — the next compliance change is what brings the provider back into sync, so a provider that was unreachable during an update stays stale until something else moves.
This is the first and most consequential decision, and it is dictated by the provider, not by preference: the identifier has to be one the provider's enforcement point can actually observe. An enforcement point cannot match on something it never sees.
EDAMAME can supply the following per device:
| Identifier | What it is | Where it comes from |
|---|---|---|
| Public IP (v4 and v6) | The address the device presents to the internet | The agent resolves it against an external echo service |
| MAC address | The hardware address of the device's default-route interface | Read locally by the agent |
| EDAMAME device ID | A stable identifier EDAMAME assigns | EDAMAME |
| Username | The identity the device is enrolled under | EDAMAME enrollment |
| Vendor peer IDs | Identities belonging to a third-party client installed on the device | Read out of that client by the agent, which requires elevated privileges |
The three sections below work through what each of these implies, using the integrations that already exist as the worked examples.
Worked example: Microsoft Entra ID. Also Google Cloud Identity, GitHub, GitLab.
The enforcement point sits in the network path between the device and a cloud resource — an identity provider, a SaaS front door, a secure web gateway. What it reliably observes is the public IP address the device connects from. So that is what the integration keys on, and it is why the EDAMAME agent resolves its own public address rather than reporting a local interface address.
The container is a list of IP ranges that policy can reference: an Entra ID named location, a Google trusted-IP list, a GitHub or GitLab allow-list. EDAMAME rewrites that list to the full set of currently compliant devices on every change, so the configuration declares the whole-list mode and uses a placeholder that expands to the entire membership rather than to one device.
What to watch for when integrating a product in this family:
- Shared egress collapses devices together. Every device behind one NAT presents the same public address, so allowing one allows all of them. On carrier-grade NAT it can extend to strangers. This archetype grants network-location trust, not device trust; treat it as one factor among several.
- Addresses churn. A device that changes network gets a new address and needs a fresh report before it regains access. Roaming users feel this as intermittent lockouts.
- IPv6 usually wins. When a device has both, the v6 address is the one used. A provider whose allow-list is v4-only needs the configuration to ask for v4 explicitly.
- Range notation varies. Some providers reject a bare address and require CIDR notation, which the engine handles per provider.
Worked example: Fortinet FortiGate.
Here the enforcement point sits on the local network segment. A firewall or NAC appliance on the same Layer 2 domain as the device sees its MAC address in every frame, long before any IP-level decision. That makes the MAC the natural key, and it is a genuinely stronger one than a public IP: it identifies the device rather than the network it happens to sit behind.
The container is a named group of address objects, and this archetype usually needs two calls rather than one, because the group can only reference objects that already exist:
- Create an address object for the device, holding its MAC.
- Rewrite the group's membership to the full set of compliant devices.
Your firewall policy then uses that group as its source criterion.
What to watch for:
- Layer 2 adjacency is mandatory. The appliance only sees a device's own MAC if the device is on a directly attached interface or VLAN. Traffic arriving through another router carries that router's MAC, and the match silently fails. This is a property of MAC matching, not of the integration, and it is the single most common reason a device is compliant in EDAMAME yet unmatched on the wire.
- The appliance has to be reachable from EDAMAME. EDAMAME's backend calls the management API, so that interface needs an ingress path from outside. Exposing it directly is one option, and restricting the API account to the addresses EDAMAME calls from keeps that narrow. The other is to put a reverse proxy in front of it and point the integration at the proxy — the same approach used for self-hosted Netbird instances. Either way the engine only needs a reachable HTTPS endpoint that speaks the vendor's API; it does not care what sits in front of it.
- Self-signed certificates are normal here. Management interfaces rarely carry a publicly trusted certificate, so the engine can be told to accept the presented certificate without validating it. This is safe on a management path you control and would otherwise block the integration entirely.
- Randomized MAC addresses are handled, but cost a propagation delay. The agent re-reads the MAC of the default-route interface continuously, exactly as it does the IP, and reports it on the next score report. A rotation therefore updates the existing address object rather than orphaning it — the object is named after the device, not after its MAC. What it does cost is a window of a few minutes between the device joining a network under a new MAC and the appliance learning it, during which the old object no longer matches. Disabling randomization for the corporate SSID removes that window but is not required for the integration to work.
- Mobile devices have no MAC to match. The agent reports an empty MAC on iOS and Android, so this archetype cannot cover them at all. That is a platform restriction rather than a configuration one.
Worked examples: NetBird, Tailscale, Netskope.
In this family the enforcement point is the vendor's own client, running on the device. It does not identify devices by IP or MAC at all, but by an identity it issued itself — a peer ID, a node ID, a device UID. EDAMAME therefore has to learn that identity before it can act on it.
That is what makes this archetype structurally different from the other two: it imposes requirements on the device, not just on the backend. The EDAMAME agent has to read the identity out of the other vendor's client, which means:
- the vendor's client must be installed and connected, and
- the EDAMAME agent must run with elevated privileges to read from it.
There is no fallback to IP or MAC when that identity is missing. EDAMAME holds both for every device, but an enforcement point that addresses devices by its own identifier cannot be handed an address it has no way to resolve, so falling back would not be a degraded match — it would be no match at all. A device that fails either requirement is therefore not addressable, and is left out of whatever set EDAMAME writes.
Being left out is not neutral. It costs the device exactly what the container grants: omission from a NetBird group, deauthorization from a Tailscale tailnet (EDAMAME deauthorizes every authorized device outside the compliant set, so a working device is actively kicked off rather than passed over), or an untagged device denied by an allow-on-tag Netskope policy.
Design a new integration in this family the same way. Failing towards denial is the property to preserve: a device EDAMAME cannot vouch for must not stay authorized because the agent went quiet. The price is that the symptom is indistinguishable from a device that never had the client, which is the first thing to check when devices are compliant in EDAMAME but never appear at the provider.
The container varies more in this family, and the variation is worth knowing:
- Group membership — NetBird. EDAMAME maintains a group, your policy interprets it.
- Direct device authorization — Tailscale. EDAMAME authorizes and deauthorizes devices outright, with no intervening policy of yours. This is the most powerful and the least reversible model; read that guide's prerequisites first.
- Device tagging — Netskope. EDAMAME applies and removes a tag, one device at a time, and your Netskope policy keyed on that tag makes the decision. Because it operates per device rather than replacing a whole list, it is also the one integration that preserves tags applied by other tools.
Adding a provider to this family is the largest piece of work of the three, because it needs agent-side discovery code shipped in an EDAMAME release before any backend configuration can reference the identity.
Pulling the three archetypes together, a product can be integrated when its API exposes an endpoint meeting all of the following. If any one is missing, the integration is not possible as a configuration change and needs product work.
A mutable container that policy can key on. A list, group, tag, or flag whose contents EDAMAME can change, and which the provider's own policy engine can reference as a condition. An API that only reports state, or that requires EDAMAME to rewrite the policy itself, does not qualify.
A stable address for that container. The operator creates the container once and supplies its name or ID during setup. EDAMAME writes into an existing container; it does not create the policy object, so that a misconfiguration cannot silently produce a second, unreferenced container that appears to work.
Acceptance of an identifier EDAMAME can supply. One of the identifiers in the table above. In particular, a provider that keys on an identity issued by its own client needs that identity to be readable on the device.
Write access under a long-lived, non-interactive credential. A service account API token or an OAuth client-credentials pair. Anything requiring a human at login time, an interactive consent screen, or per-call MFA cannot work in a backend that runs unattended. Scope the credential to only the objects it must change.
Reachability from EDAMAME's backend. Calls originate from EDAMAME's cloud backend, not from the device. A SaaS API satisfies this by default; on-premise equipment needs an ingress path, as described in archetype 2.
Idempotent writes. The same membership may be pushed repeatedly, and in whole-list mode the entire set is rewritten on every change. Re-adding a member that is already present must succeed rather than error, and rewriting a list to its current value must be a no-op.
Two further properties are not strictly required but change the quality of the result markedly. An endpoint that merges rather than replaces lets EDAMAME coexist with other tools writing to the same object, instead of claiming exclusive ownership. And an endpoint with no low ceiling on collection size avoids the failure mode where a device cannot be added because some per-object limit is already reached.
For a custom integration — a provider you configure yourself in EDAMAME Hub — the whole of the above is expressed as a single JSON document plus the secrets you enter alongside it. Nothing needs to be released by EDAMAME. The JSON configuration guide is the reference: it covers the selector, the whole-list versus per-device modes, authentication, REST and GraphQL actions, and the full placeholder vocabulary.
For a built-in provider — one selectable by name in EDAMAME Hub, like those on
the Home page — the same JSON is published in the public integrations
repository, and EDAMAME additionally ships the provider's entry in the Hub setup
form so the right credential fields are prompted for. Two optional pieces are added
when the declarative form is not enough: purpose-written backend logic, for
providers whose API needs more than a straightforward sequence of calls, and
agent-side discovery, for the archetype 3 providers whose identity has to be read
off the device.
If you have a provider you would like supported, the useful thing to send is not a feature request but the answers to the previous section: which container its policy can key on, which identifier it matches devices by, and how a service account authenticates against it.
Setup guides
Reference
Links