Skip to content

FirewallPolicyOrdering

Package: @alchemy-unifi/network@0.1.2-nightly.20261027100339.gc70dd7b.0 · Source: FirewallPolicyOrdering.ts

The order the policies from one zone to another are evaluated in, first match wins. Each zone pair has two buckets, with the system policies between them: beforeSystemDefined and afterSystemDefined. System policies can’t be reordered, and policies UniFi derives from UI objects aren’t ordered at all.

The zone pair is the identity, so an existing ordering is adopted without --adopt, and deleting the resource leaves the order as it is. Listing policies makes the ordering depend on them, so it’s applied after they’re created or moved.

An allow before a block

import * as UnifiNetwork from "@alchemy-unifi/network";
import * as Alchemy from "alchemy";
import * as Effect from "effect/Effect";
export default Alchemy.Stack(
"HomeNetwork",
{ providers: UnifiNetwork.providers(), state: UnifiNetwork.state() },
Effect.gen(function* () {
const { INTERNAL } = UnifiNetwork.FirewallZone;
const iot = yield* UnifiNetwork.FirewallZone("iot-zone", { name: "IoT" });
const policy = {
enabled: true,
source: { zone: INTERNAL },
destination: { zone: iot },
loggingEnabled: false,
} as const;
const allowHomeAssistant = yield* UnifiNetwork.FirewallPolicy("allow-ha", {
...policy,
name: "Allow Home Assistant",
action: { type: "ALLOW", allowReturnTraffic: true },
ipProtocolScope: { ipVersion: "IPV4_AND_IPV6" },
});
const blockTheRest = yield* UnifiNetwork.FirewallPolicy("block-rest", {
...policy,
name: "Block the rest",
action: { type: "BLOCK" },
ipProtocolScope: { ipVersion: "IPV4_AND_IPV6" },
});
yield* UnifiNetwork.FirewallPolicyOrdering("internal-to-iot", {
source: INTERNAL,
destination: iot,
beforeSystemDefined: [allowHomeAssistant, blockTheRest],
});
}),
);

UniFi rejects an ordering that leaves out any of the pair’s policies, so unlisted ones (e.g. made in the UI) are kept after the listed ones in each bucket, in their current relative order, with a warning. Set exclusive: true to fail instead.

A policy the ordering listed before but no longer does is kept quietly, even when exclusive: it’s usually being deleted in the same deploy, which runs after the ordering is applied. So removing a policy and its place in the ordering in one deploy works.

A Gateway → Gateway ordering keeps UnifiNetwork.state()’s policies where they are, even when exclusive.