-- +goose Up -- +goose StatementBegin -- Network interfaces declared by the operator. The kernel-side -- interfaces (eth0, eth1, ...) appear here via "discovery" — same -- name as the OS device. VLAN/bond/bridge interfaces are operator- -- created and the runtime renderer (Phase-3) will translate them -- into /etc/systemd/network/*.network files. -- -- role drives where the firewall/HAProxy generators expect this -- interface to live: 'wan' = public-facing, 'lan' = internal, -- 'dmz' = quarantined, 'mgmt' = admin-only, 'cluster' = peer-mTLS. CREATE TABLE IF NOT EXISTS network_interfaces ( id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL, type TEXT NOT NULL DEFAULT 'ethernet', parent TEXT, vlan_id INTEGER, role TEXT NOT NULL DEFAULT 'lan', mtu INTEGER, active BOOLEAN NOT NULL DEFAULT TRUE, description TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT network_interfaces_name_unique UNIQUE (name), CONSTRAINT network_interfaces_type_check CHECK (type IN ('ethernet', 'vlan', 'bond', 'bridge', 'wireguard')), CONSTRAINT network_interfaces_role_check CHECK (role IN ('wan', 'lan', 'dmz', 'mgmt', 'cluster')), CONSTRAINT network_interfaces_vlan_check CHECK ( (type = 'vlan' AND vlan_id BETWEEN 1 AND 4094 AND parent IS NOT NULL) OR (type <> 'vlan') ) ); CREATE INDEX IF NOT EXISTS idx_network_interfaces_role ON network_interfaces (role); CREATE INDEX IF NOT EXISTS idx_network_interfaces_active ON network_interfaces (active) WHERE active; -- IP addresses bound to a declared interface. -- -- is_vip flags addresses that should follow the cluster's active -- node (Phase-3 floating-IP / VRRP-via-hoster-API logic). vip_priority -- gives the failover preference (higher wins); ignored for non-VIP -- rows. -- -- The same address can live on the same interface only once -- (UNIQUE), but the same address may legitimately appear on -- different interfaces (e.g. as anycast endpoint). CREATE TABLE IF NOT EXISTS ip_addresses ( id BIGSERIAL PRIMARY KEY, interface_id BIGINT NOT NULL REFERENCES network_interfaces(id) ON DELETE CASCADE, address TEXT NOT NULL, prefix INTEGER NOT NULL, is_vip BOOLEAN NOT NULL DEFAULT FALSE, vip_priority INTEGER, description TEXT, active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT ip_addresses_iface_addr_unique UNIQUE (interface_id, address), CONSTRAINT ip_addresses_prefix_check CHECK (prefix BETWEEN 0 AND 128) ); CREATE INDEX IF NOT EXISTS idx_ip_addresses_iface ON ip_addresses (interface_id); CREATE INDEX IF NOT EXISTS idx_ip_addresses_vip ON ip_addresses (is_vip) WHERE is_vip; CREATE INDEX IF NOT EXISTS idx_ip_addresses_active ON ip_addresses (active) WHERE active; -- +goose StatementEnd -- +goose Down -- +goose StatementBegin DROP TABLE IF EXISTS ip_addresses; DROP TABLE IF EXISTS network_interfaces; -- +goose StatementEnd