diff --git a/northd/lrouter.dl b/northd/lrouter.dl
index b2b429af3c96..e4e5cbf9f212 100644
--- a/northd/lrouter.dl
+++ b/northd/lrouter.dl
@@ -23,7 +23,7 @@ import lswitch
 function is_enabled(lr: nb::Logical_Router): bool { is_enabled(lr.enabled) }
 function is_enabled(lrp: nb::Logical_Router_Port): bool { is_enabled(lrp.enabled) }
 function is_enabled(rp: RouterPort): bool { rp.lrp.is_enabled() }
-function is_enabled(rp: Ref<RouterPort>): bool { rp.lrp.is_enabled() }
+function is_enabled(rp: Intern<RouterPort>): bool { rp.lrp.is_enabled() }
 
 /* default logical flow prioriry for distributed routes */
 function dROUTE_PRIO(): bit<32> = 400
@@ -575,7 +575,7 @@ RouterPortHasBfd(lrp_uuid, false) :-
 
 /* FIXME: what should happen when extract_lrp_networks fails? */
 /* RouterPort relation collects all attributes of a logical router port */
-relation &RouterPort(
+typedef RouterPort = RouterPort {
     lrp:              nb::Logical_Router_Port,
     json_name:        string,
     networks:         lport_addresses,
@@ -584,17 +584,22 @@ relation &RouterPort(
     peer:             RouterPeer,
     mcast_cfg:        Ref<McastPortCfg>,
     sb_options:       Map<string,string>,
-    has_bfd:          bool)
-
-&RouterPort(.lrp                = lrp,
-            .json_name          = json_string_escape(lrp.name),
-            .networks           = networks,
-            .router             = router,
-            .is_redirect        = is_redirect,
-            .peer               = peer,
-            .mcast_cfg          = mcast_cfg,
-            .sb_options         = sb_options,
-            .has_bfd            = has_bfd) :-
+    has_bfd:          bool
+}
+
+relation RouterPort[Intern<RouterPort>]
+
+RouterPort[RouterPort{
+               .lrp                = lrp,
+               .json_name          = json_string_escape(lrp.name),
+               .networks           = networks,
+               .router             = router,
+               .is_redirect        = is_redirect,
+               .peer               = peer,
+               .mcast_cfg          = mcast_cfg,
+               .sb_options         = sb_options,
+               .has_bfd            = has_bfd
+           }.intern()] :-
     nb::Logical_Router_Port[lrp],
     Some{var networks} = extract_lrp_networks(lrp.mac, lrp.networks),
     LogicalRouterPort(lrp._uuid, lrouter_uuid),
@@ -605,13 +610,13 @@ relation &RouterPort(
     RouterPortSbOptions(lrp._uuid, sb_options),
     RouterPortHasBfd(lrp._uuid, has_bfd).
 
-relation RouterPortNetworksIPv4Addr(port: Ref<RouterPort>, addr: ipv4_netaddr)
+relation RouterPortNetworksIPv4Addr(port: Intern<RouterPort>, addr: ipv4_netaddr)
 
 RouterPortNetworksIPv4Addr(port, addr) :-
     port in &RouterPort(.networks = networks),
     var addr = FlatMap(networks.ipv4_addrs).
 
-relation RouterPortNetworksIPv6Addr(port: Ref<RouterPort>, addr: ipv6_netaddr)
+relation RouterPortNetworksIPv6Addr(port: Intern<RouterPort>, addr: ipv6_netaddr)
 
 RouterPortNetworksIPv6Addr(port, addr) :-
     port in &RouterPort(.networks = networks),
@@ -733,7 +738,7 @@ RouterStaticRoute_(.router = router,
 typedef route_dst = RouteDst {
     nexthop: v46_ip,
     src_ip: v46_ip,
-    port: Ref<RouterPort>,
+    port: Intern<RouterPort>,
     ecmp_symmetric_reply: bool
 }
 
diff --git a/northd/lswitch.dl b/northd/lswitch.dl
index c089fadac863..aff265bbf82c 100644
--- a/northd/lswitch.dl
+++ b/northd/lswitch.dl
@@ -27,7 +27,7 @@ function is_enabled(lsp: Ref<nb::Logical_Switch_Port>): bool { lsp.deref().is_en
 function is_enabled(sp: SwitchPort): bool { sp.lsp.is_enabled() }
 function is_enabled(sp: Intern<SwitchPort>): bool { sp.lsp.is_enabled() }
 
-relation SwitchRouterPeerRef(lsp: uuid, rport: Option<Ref<RouterPort>>)
+relation SwitchRouterPeerRef(lsp: uuid, rport: Option<Intern<RouterPort>>)
 
 SwitchRouterPeerRef(lsp, Some{rport}) :-
     SwitchRouterPeer(lsp, _, lrp),
@@ -555,7 +555,7 @@ typedef SwitchPort = SwitchPort {
     lsp:                        nb::Logical_Switch_Port,
     json_name:                  string,
     sw:                         Intern<Switch>,
-    peer:                       Option<Ref<RouterPort>>,
+    peer:                       Option<Intern<RouterPort>>,
     static_addresses:           Vec<lport_addresses>,
     dynamic_address:            Option<lport_addresses>,
     static_dynamic_mac:         Option<eth_addr>,
diff --git a/northd/ovn_northd.dl b/northd/ovn_northd.dl
index c4d46b2a8406..9e53821719c9 100644
--- a/northd/ovn_northd.dl
+++ b/northd/ovn_northd.dl
@@ -192,7 +192,7 @@ OutProxy_Port_Binding(._uuid              = lsp._uuid,
             Some{"router"} -> match ((l3dgw_port, opt_chassis, peer)) {
                                  (None, None, _) -> set_empty(),
                                  (_, _, None) -> set_empty(),
-                                 (_, _, Some{rport}) -> get_nat_addresses(deref(rport))
+                                 (_, _, Some{rport}) -> get_nat_addresses(rport)
                               },
             Some{nat_addresses} -> {
                 /* Only accept manual specification of ethernet address
@@ -233,7 +233,7 @@ OutProxy_Port_Binding(._uuid              = lsp._uuid,
             (rport.router.options.contains_key("chassis") and
              not sw.localnet_ports.is_empty())) {
             false -> set_empty(),
-            true -> set_singleton(get_garp_nat_addresses(deref(rport)))
+            true -> set_singleton(get_garp_nat_addresses(rport))
         },
         None -> set_empty()
     },
@@ -316,7 +316,7 @@ function get_router_load_balancer_ips(router: Intern<Router>) :
  * external IP addresses of all NAT rules defined on that router, and all
  * of the IP addresses used in load balancer VIPs defined on that router.
  */
-function get_nat_addresses(rport: RouterPort): Set<string> =
+function get_nat_addresses(rport: Intern<RouterPort>): Set<string> =
 {
     var addresses = set_empty();
     var has_redirect = rport.router.l3dgw_port.is_some();
@@ -391,7 +391,7 @@ function get_nat_addresses(rport: RouterPort): Set<string> =
     }
 }
 
-function get_garp_nat_addresses(rport: RouterPort): string = {
+function get_garp_nat_addresses(rport: Intern<RouterPort>): string = {
     var garp_info = ["${rport.networks.ea}"];
     for (ipv4_addr in rport.networks.ipv4_addrs) {
         garp_info.push("${ipv4_addr.addr}")
@@ -4037,7 +4037,7 @@ for (SwitchPortStaticAddresses(.port = &SwitchPort{.lsp = lsp, .json_name = json
 /* Returns 'true' if the IP 'addr' is on the same subnet with one of the
  * IPs configured on the router port.
  */
-function lrouter_port_ip_reachable(rp: Ref<RouterPort>, addr: v46_ip): bool {
+function lrouter_port_ip_reachable(rp: Intern<RouterPort>, addr: v46_ip): bool {
     match (addr) {
         IPv4{ipv4} -> {
             for (na in rp.networks.ipv4_addrs) {
@@ -4086,7 +4086,7 @@ UniqueFlow[Flow{.logical_datapath = sw._uuid,
  * router port.
  * Priority: 80.
  */
-function get_arp_forward_ips(rp: Ref<RouterPort>): (Set<string>, Set<string>) = {
+function get_arp_forward_ips(rp: Intern<RouterPort>): (Set<string>, Set<string>) = {
     var all_ips_v4 = set_empty();
     var all_ips_v6 = set_empty();
 
@@ -6322,7 +6322,7 @@ for (&Router(._uuid = lr_uuid))
  * the following rule.
  */
 relation Route(key:         route_key,       // matching criteria
-               port:        Ref<RouterPort>, // output port
+               port:        Intern<RouterPort>, // output port
                src_ip:      v46_ip,          // source IP address for output
                gateway:     Option<v46_ip>) // next hop (unless being delivered)
 
