@takodotid/circuit 0.36.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/chunks/index-6wb41q67.js +62 -0
  2. package/dist/chunks/{index-2r4bbhac.js → index-yzw86rc8.js} +9 -65
  3. package/dist/chunks/{server-gzsf5me5.js → server-cxr9ynxc.js} +1 -1
  4. package/dist/cli/index.js +110 -13
  5. package/dist/helpers/index.js +227 -1
  6. package/dist/types/core/address-space.d.ts +1 -1
  7. package/dist/types/core/address.d.ts +1 -1
  8. package/dist/types/core/cabling.d.ts +3 -3
  9. package/dist/types/core/cross-checks.d.ts +3 -3
  10. package/dist/types/core/define.d.ts +2 -2
  11. package/dist/types/core/secrets.d.ts +2 -2
  12. package/dist/types/core/topology.d.ts +2 -2
  13. package/dist/types/core/validator.d.ts +1 -1
  14. package/dist/types/helpers/bgp.d.ts +48 -5
  15. package/dist/types/helpers/cables.d.ts +70 -0
  16. package/dist/types/helpers/checks.d.ts +1 -1
  17. package/dist/types/helpers/exchange.d.ts +3 -3
  18. package/dist/types/helpers/filters.d.ts +1 -1
  19. package/dist/types/helpers/index.d.ts +14 -10
  20. package/dist/types/helpers/route-server.d.ts +2 -2
  21. package/dist/types/helpers/upstreams.d.ts +156 -0
  22. package/dist/types/index.d.ts +8 -6
  23. package/dist/types/integrations/librenms.d.ts +2 -2
  24. package/dist/types/integrations/netbox.d.ts +1 -1
  25. package/dist/types/integrations/peeringdb.d.ts +1 -1
  26. package/dist/types/integrations/prometheus.d.ts +2 -2
  27. package/dist/types/integrations/registries.d.ts +2 -2
  28. package/dist/types/platforms/bgp-wait.d.ts +7 -1
  29. package/dist/types/platforms/derive.d.ts +1 -1
  30. package/dist/types/platforms/fortios/firewall.d.ts +2 -2
  31. package/dist/types/platforms/fortios/index.d.ts +4 -4
  32. package/dist/types/platforms/fortios/names.d.ts +1 -1
  33. package/dist/types/platforms/fortios/plan.d.ts +1 -1
  34. package/dist/types/platforms/fortios/render.d.ts +1 -1
  35. package/dist/types/platforms/fortios/routing.d.ts +2 -2
  36. package/dist/types/platforms/index.d.ts +1 -1
  37. package/dist/types/platforms/junos/bgp.d.ts +2 -2
  38. package/dist/types/platforms/junos/index.d.ts +4 -4
  39. package/dist/types/platforms/junos/render.d.ts +1 -1
  40. package/dist/types/platforms/line-diff.d.ts +1 -1
  41. package/dist/types/platforms/platform.d.ts +17 -5
  42. package/dist/types/platforms/raisecom-ros/index.d.ts +4 -4
  43. package/dist/types/platforms/raisecom-ros/render.d.ts +1 -1
  44. package/dist/types/platforms/routeros/index.d.ts +12 -5
  45. package/dist/types/platforms/routeros/plan.d.ts +1 -1
  46. package/dist/types/platforms/routeros/render.d.ts +1 -1
  47. package/dist/types/platforms/vrp/index.d.ts +4 -4
  48. package/dist/types/platforms/vrp/render.d.ts +1 -1
  49. package/dist/types/schema/device.d.ts +6 -6
  50. package/dist/types/schema/firewall.d.ts +1 -1
  51. package/dist/types/schema/index.d.ts +8 -8
  52. package/dist/types/schema/interfaces.d.ts +5 -1
  53. package/dist/types/schema/policy.d.ts +1 -1
  54. package/dist/types/schema/routing.d.ts +1 -1
  55. package/dist/types/schema/system.d.ts +2 -2
  56. package/dist/types/transport/console.d.ts +1 -1
  57. package/dist/types/transport/ssh.d.ts +1 -1
  58. package/package.json +2 -2
  59. package/src/cli/index.ts +55 -4
  60. package/src/core/cabling.ts +4 -1
  61. package/src/core/cross-checks.ts +6 -1
  62. package/src/core/topology.ts +4 -3
  63. package/src/helpers/bgp.ts +104 -2
  64. package/src/helpers/cables.ts +91 -0
  65. package/src/helpers/index.ts +6 -2
  66. package/src/helpers/upstreams.ts +306 -0
  67. package/src/index.ts +3 -0
  68. package/src/platforms/bgp-wait.ts +24 -1
  69. package/src/platforms/platform.ts +9 -0
  70. package/src/platforms/routeros/TRAPS.md +1 -0
  71. package/src/platforms/routeros/index.ts +57 -10
  72. package/src/schema/interfaces.ts +4 -0
  73. package/src/studio/data.ts +3 -0
  74. package/src/studio/public/tables.js +15 -1
@@ -1,4 +1,4 @@
1
- import type { Device } from "../schema";
1
+ import type { Device } from "../schema/index.js";
2
2
  /** One address or range in a segment, and what holds it. */
3
3
  export type Use = {
4
4
  /** An address, or `first-last` for a range. */
@@ -1,4 +1,4 @@
1
- import type { Family, IP, IPv4, Prefix } from "../schema";
1
+ import type { Family, IP, IPv4, Prefix } from "../schema/index.js";
2
2
  /** Address helpers, for checks of your own. */
3
3
  export declare class Address {
4
4
  /** IPv4 space another network cannot reach: this host, private, shared, loopback, link-local and documentation, after RFC 6890. */
@@ -1,5 +1,5 @@
1
- import type { Device } from "../schema";
2
- import type { Finding } from "./define";
1
+ import type { Device } from "../schema/index.js";
2
+ import type { Finding } from "./define.js";
3
3
  /** A device one of our ports sees at the far end of its cable, over LLDP. */
4
4
  export type Neighbor = {
5
5
  /** Our port, by Circuit's name, such as `10g-1`. */
@@ -28,7 +28,7 @@ export declare class Cabling {
28
28
  /**
29
29
  * Warnings where LLDP and `link` disagree, from each device's own view: a cable in another port than `link` names, a cable `link` names that LLDP does not see, and another of our devices on a port without `link`.
30
30
  *
31
- * Only ports where both ends run LLDP, by the config, are compared, and only on devices whose neighbors were read. LLDP can be off at the far end, so each is a warning.
31
+ * Only ports where both ends run LLDP, by the config, are compared, and only on devices whose neighbors were read. A link `through` a carrier is not compared. LLDP can be off at the far end, so each is a warning.
32
32
  */
33
33
  static compare(devices: readonly Device[], neighbors: ReadonlyMap<string, readonly Neighbor[]>): Finding[];
34
34
  /** A system name is the device's name, alone or with a domain after it. */
@@ -1,5 +1,5 @@
1
- import type { Device } from "../schema";
2
- import type { Finding } from "./define";
1
+ import type { Device } from "../schema/index.js";
2
+ import type { Finding } from "./define.js";
3
3
  export declare class CrossChecks {
4
4
  /** Every rule across devices. */
5
5
  static run(devices: readonly Device[]): Finding[];
@@ -15,7 +15,7 @@ export declare class CrossChecks {
15
15
  private static plainAddresses;
16
16
  /** Why the two ends of a cable cannot reach each other by address, or undefined when they can. */
17
17
  private static addressDisagreement;
18
- /** A link names a device and port that exist, the far end links back, and both ends carry the same VLANs the same way, with the same MTU, speed and addresses. */
18
+ /** A link names a device and port that exist, the far end links back through the same carrier, and both ends carry the same VLANs the same way, with the same MTU, speed and addresses. */
19
19
  private static checkLinks;
20
20
  /** No address is held by two devices in one VRF. Two loopbacks may hold one on purpose, for anycast, so that is a warning. */
21
21
  private static checkAddresses;
@@ -1,5 +1,5 @@
1
- import type { ModelName, PlatformName, PortName } from "../platforms/catalog";
2
- import type { Device, Secret } from "../schema";
1
+ import type { ModelName, PlatformName, PortName } from "../platforms/catalog.js";
2
+ import type { Device, Secret } from "../schema/index.js";
3
3
  /** A value resolved by name when a device is applied, from the environment or `.env` beside the config, and through 1Password when that value is an `op://` reference. Never the value itself. */
4
4
  export declare const secret: (name: string) => Secret;
5
5
  /** A value read from a file when a device is applied, such as a private key in PEM. The path is relative to the config file; keep the file out of git. */
@@ -1,5 +1,5 @@
1
- import type { Network } from "./define";
2
- import type { Console, Device, IP, Secret } from "../schema";
1
+ import type { Network } from "./define.js";
2
+ import type { Console, Device, IP, Secret } from "../schema/index.js";
3
3
  /** How a device is logged in to: its management address and SSH port, its connection user and that user's password, and its console. */
4
4
  export type DeviceLogin = {
5
5
  /** Absent for a device reached only through its console. */
@@ -1,5 +1,5 @@
1
- import type { Device, Routed } from "../schema";
2
- import type { Finding } from "./define";
1
+ import type { Device, Routed } from "../schema/index.js";
2
+ import type { Finding } from "./define.js";
3
3
  /** One view of the network. Every edge belongs to one. */
4
4
  export type Layer = "l1" | "l2" | "l3" | "bgp" | "ospf" | "tunnels";
5
5
  /** One of our devices, a segment several of them share, or something outside the network. */
@@ -1,4 +1,4 @@
1
- import type { Finding, Network } from "./define";
1
+ import type { Finding, Network } from "./define.js";
2
2
  export declare class Validator {
3
3
  static run(network: Network): Finding[];
4
4
  private static checkDevice;
@@ -1,5 +1,5 @@
1
- import type { Community } from "../core/define";
2
- import type { Device, PolicyRule, Prefix, StandardCommunity } from "../schema";
1
+ import type { Community } from "../core/define.js";
2
+ import type { Device, IP, LargeCommunity, PolicyRule, Prefix, StandardCommunity } from "../schema/index.js";
3
3
  /** The prefix lengths a BGP import accepts. */
4
4
  export type SanityLengths = {
5
5
  /** Shortest and longest IPv4 prefix accepted. /8 to /24 when absent. */
@@ -13,6 +13,33 @@ export type SanityLengths = {
13
13
  max: number;
14
14
  };
15
15
  };
16
+ /** A prefix we announce, and what it is. */
17
+ export type Announced = {
18
+ prefix: Prefix;
19
+ description?: string;
20
+ };
21
+ /** What `Bgp.learn` builds an import from. */
22
+ export type LearnOptions<P extends string> = {
23
+ /** Rules of your own, first. */
24
+ before?: readonly PolicyRule<P, never>[];
25
+ /** A policy holding `Bgp.sanity()` to call. The sanity rules are written in the import itself when absent. */
26
+ sanity?: P;
27
+ /** The source address for routes of its family, one per family. */
28
+ sources?: readonly IP[];
29
+ /** Preference for the routes learned. Higher wins. */
30
+ local_pref?: number;
31
+ /** Where the route came from: a community to add, or a rule, such as `communityScheme(...).tag(...)`. */
32
+ tag?: StandardCommunity | LargeCommunity | PolicyRule<never, never>;
33
+ };
34
+ /** What `Bgp.defaultOnly` sets on the default routes it takes. */
35
+ export type DefaultOnlyOptions = {
36
+ /** Preference for the default route. Higher wins. */
37
+ local_pref?: number;
38
+ /** Shown beside each rule. `Default route, ipv4` and `Default route, ipv6` when absent. */
39
+ description?: string;
40
+ /** The source address for the default of its family, one per family. */
41
+ sources?: readonly IP[];
42
+ };
16
43
  /** Kinds of neighbor and the number each is tagged with, such as `{ transit: 1, customer: 3 }`. */
17
44
  export type CommunityClasses = Readonly<Record<string, number>>;
18
45
  /**
@@ -55,16 +82,32 @@ export type CommunityScheme<Asn extends number = number, LearnedFrom extends num
55
82
  };
56
83
  /** The class names a scheme declares, or any name when it declares none. */
57
84
  type ClassName<Classes extends CommunityClasses> = {} extends Classes ? string : Extract<keyof Classes, string>;
58
- /** BGP policy: an import sanity policy, a community scheme, and helpers that read a device's BGP neighbors. */
85
+ /** BGP policy: an import sanity policy, the shapes most imports and exports take, a community scheme, and helpers that read a device's BGP neighbors. */
59
86
  export declare class Bgp {
60
87
  private static readonly PREPENDS;
88
+ /** The default route of each family. */
89
+ private static readonly DEFAULTS;
61
90
  /** Route policy rules rejecting what no neighbor should send: prefixes too short or too long for the global table, and RPKI invalid. Place it first in an import, or as a policy other imports call. */
62
91
  static sanity(lengths?: SanityLengths): PolicyRule<never, never>[];
92
+ /** An export policy that announces each prefix, at exactly its length, then rejects everything else. The last rule is the unconditional reject `Checks.exportsEndInReject` looks for. */
93
+ static announce(prefixes: readonly Announced[]): PolicyRule<never, never>[];
94
+ /**
95
+ * An import policy that takes a neighbor's routes: `before` first, then the sanity rules, then each source address and the local preference, then the tag, then accept.
96
+ *
97
+ * `sanity` names a policy holding `Bgp.sanity()` to call, so several imports share one; without it, the sanity rules are written here. `tag` is a community to add, or a rule such as `communityScheme(...).tag(...)`. Each of `sources` is the source address for routes of its own family.
98
+ */
99
+ static learn<const P extends string = never>(options?: LearnOptions<P>): PolicyRule<P, never>[];
100
+ /**
101
+ * Import rules that take the default route, `0.0.0.0/0` and `::/0`, from a transit. Any other route reaches the next rule, or is rejected when there is none. Each of `sources` is the source address for the default of its own family.
102
+ */
103
+ static defaultOnly(options?: DefaultOnlyOptions): PolicyRule<never, never>[];
104
+ /** A rule adding one community, standard or large. */
105
+ private static adding;
63
106
  /** Each BGP neighbor's own policies and role, or its group's. */
64
107
  static neighborPolicies(device: Device): {
65
108
  name: string;
66
- address: import("..").IP;
67
- role: import("..").BgpRole | undefined;
109
+ address: IP;
110
+ role: import("../index.js").BgpRole | undefined;
68
111
  import: string | undefined;
69
112
  export: string | undefined;
70
113
  }[];
@@ -0,0 +1,70 @@
1
+ import { type ModelName, type PlatformName, type PortName } from "../platforms/catalog.js";
2
+ /** One end of a cable: the device, and its platform and model, which name its ports. */
3
+ export type CableEnd<P extends PlatformName, M extends ModelName<P>> = {
4
+ device: string;
5
+ platform: P;
6
+ model: M;
7
+ };
8
+ /** The far end of a port, for its description: the device, the port by Circuit's name, and by the name printed on the device. */
9
+ export type FarEnd = {
10
+ device: string;
11
+ port: string;
12
+ label: string;
13
+ };
14
+ /** What every cable of one entry shares. */
15
+ type Shared = {
16
+ /** What is between the two ends, such as a carrier's local loop. Both ends get it on their `link`. */
17
+ through?: string;
18
+ /** A port's description from its far end. The far device and the far port's label when absent, such as `firewall-1 port6`. */
19
+ describe?: (far: FarEnd) => string;
20
+ };
21
+ /** A port a cable builds: what it is, the LAG it is in, and the other end. */
22
+ type CablePort<L extends string> = {
23
+ description: string;
24
+ link: {
25
+ device: string;
26
+ port: string;
27
+ through?: string;
28
+ };
29
+ lag?: L;
30
+ };
31
+ /** Ports for both ends of the cables between two of our devices, each cable written once. */
32
+ export declare class Cables {
33
+ /**
34
+ * One cable, both ends. Spread each end into its device's `ports`:
35
+ *
36
+ * ```ts
37
+ * const uplink = Cables.link({ a: { device: "router-1", platform: "routeros", model: "CCR2216-1G-12XS-2XQ", port: "25g-1" }, b: { ... } });
38
+ * // router-1: ports: { ...uplink.a }
39
+ * ```
40
+ */
41
+ static link<const PA extends PlatformName, const MA extends ModelName<PA>, const NA extends PortName<PA, MA>, const PB extends PlatformName, const MB extends ModelName<PB>, const NB extends PortName<PB, MB>>(spec: Shared & {
42
+ a: CableEnd<PA, MA> & {
43
+ port: NA;
44
+ };
45
+ b: CableEnd<PB, MB> & {
46
+ port: NB;
47
+ };
48
+ }): {
49
+ a: Record<NA, CablePort<never>>;
50
+ b: Record<NB, CablePort<never>>;
51
+ };
52
+ /**
53
+ * The cables of a LAG, both ends, from one list read from end A: `{ "25g-1": "10g-2" }` is a cable from A's 25g-1 to B's 10g-2. Each port is a member of its end's LAG. Spread each end into its device's `ports`; the LAG itself stays on each device.
54
+ */
55
+ static lag<const PA extends PlatformName, const MA extends ModelName<PA>, const LA extends string, const PB extends PlatformName, const MB extends ModelName<PB>, const LB extends string, const C extends Partial<Record<PortName<PA, MA>, PortName<PB, MB>>>>(spec: Shared & {
56
+ a: CableEnd<PA, MA> & {
57
+ lag: LA;
58
+ };
59
+ b: CableEnd<PB, MB> & {
60
+ lag: LB;
61
+ };
62
+ cables: C;
63
+ }): {
64
+ a: Record<keyof C & string, CablePort<LA>>;
65
+ b: Record<NonNullable<C[keyof C]> & string, CablePort<LB>>;
66
+ };
67
+ /** A port facing `port` on the far device. */
68
+ private static end;
69
+ }
70
+ export {};
@@ -1,4 +1,4 @@
1
- import type { Check } from "../core/define";
1
+ import type { Check } from "../core/define.js";
2
2
  /** Where traffic from outside, before anything filters it, may go. Every VLAN not listed as untrusted is trusted, so a VLAN added later is protected without being listed. */
3
3
  export type TrustBoundary = {
4
4
  /** VLANs carrying traffic straight from outside: transits, exchanges, an ISP handoff. */
@@ -1,6 +1,6 @@
1
- import type { Check } from "../core/define";
2
- import type { Ethertype, IP, Port, Prefix } from "../schema";
3
- import { type RouteServer } from "./route-server";
1
+ import type { Check } from "../core/define.js";
2
+ import type { Ethertype, IP, Port, Prefix } from "../schema/index.js";
3
+ import { type RouteServer } from "./route-server.js";
4
4
  /** A member of an internet exchange: a network with a port on the fabric and an address on the peering LAN. */
5
5
  export type ExchangeMember = {
6
6
  /** Lowercase, and never reused. */
@@ -1,4 +1,4 @@
1
- import type { AclRule, FilterRule, Prefix } from "../schema";
1
+ import type { AclRule, FilterRule, Prefix } from "../schema/index.js";
2
2
  /** What `Filters.antiAmplification` leaves alone. */
3
3
  export type AntiAmplificationOptions = {
4
4
  /** Prefixes inside the destinations whose UDP fragments pass: for a host that receives UDP too large for one packet, such as SIP or some VPNs. Every fragment after the first is dropped when absent. */
@@ -1,10 +1,14 @@
1
- export { Bgp } from "./bgp";
2
- export type { CommunityScheme, SanityLengths } from "./bgp";
3
- export { Checks } from "./checks";
4
- export type { TrustBoundary } from "./checks";
5
- export { Exchange } from "./exchange";
6
- export type { ExchangeMember, InternetExchange } from "./exchange";
7
- export { Filters } from "./filters";
8
- export { RouteServerConfig } from "./route-server";
9
- export type { RouteServer } from "./route-server";
10
- export type { AntiAmplificationOptions } from "./filters";
1
+ export { Bgp } from "./bgp.js";
2
+ export type { Announced, CommunityScheme, DefaultOnlyOptions, LearnOptions, SanityLengths } from "./bgp.js";
3
+ export { Cables } from "./cables.js";
4
+ export type { CableEnd, FarEnd } from "./cables.js";
5
+ export { Checks } from "./checks.js";
6
+ export type { TrustBoundary } from "./checks.js";
7
+ export { Exchange } from "./exchange.js";
8
+ export type { ExchangeMember, InternetExchange } from "./exchange.js";
9
+ export { Filters } from "./filters.js";
10
+ export { RouteServerConfig } from "./route-server.js";
11
+ export { Upstreams } from "./upstreams.js";
12
+ export type { Upstream, UpstreamCatalog, UpstreamPolicies } from "./upstreams.js";
13
+ export type { RouteServer } from "./route-server.js";
14
+ export type { AntiAmplificationOptions } from "./filters.js";
@@ -1,5 +1,5 @@
1
- import type { Family, IPv4, IPv6, Prefix } from "../schema";
2
- import type { ExchangeMember } from "./exchange";
1
+ import type { Family, IPv4, IPv6, Prefix } from "../schema/index.js";
2
+ import type { ExchangeMember } from "./exchange.js";
3
3
  /** A route server on an exchange's peering LAN: a server running BIRD 2 or OpenBGPD that passes every member's routes to every other member. */
4
4
  export type RouteServer = {
5
5
  /** Its name, such as `rs1`, written at the top of its config. */
@@ -0,0 +1,156 @@
1
+ import type { Exchange as ExchangeLan, Family, FilterRule, Gre, InterfaceAddress, IP, LargeCommunity, Neighbor, NeighborSettings, PolicyRule, Port, Secret, StandardCommunity, Vlan, VlanInterface } from "../schema/index.js";
2
+ import { type Announced } from "./bgp.js";
3
+ /** One network we exchange routes with: a transit we buy, an exchange's route servers, or a peer. */
4
+ export type Upstream = {
5
+ /** Its name as people write it, for descriptions: `Transit A`. The key when absent. */
6
+ name?: string;
7
+ asn: number;
8
+ /** What it is to us: `provider` for a transit, `rs` for an exchange's route servers, `peer` for a direct session. */
9
+ role: "provider" | "rs" | "peer";
10
+ /** Where it lands on our side: a VLAN, our addresses on it, and the switch port it arrives on. A peer over a tunnel, or over an exchange's VLAN, has none. */
11
+ handoff?: {
12
+ /** The VLAN's ID. */
13
+ vlan: number;
14
+ /** It sends its VLAN tagged: its port gets a native VLAN of its own with this ID, which nothing else is in. Untagged when absent. */
15
+ native?: number;
16
+ /** Our addresses on it. `{ address, peer }` for a point-to-point address whose far end is outside its own prefix. */
17
+ addresses: readonly (InterfaceAddress | {
18
+ address: InterfaceAddress;
19
+ peer: IP;
20
+ })[];
21
+ /** The switch port it arrives on. */
22
+ port?: {
23
+ device: string;
24
+ port: string;
25
+ storm_control?: Port<string, string, string, string>["storm_control"];
26
+ };
27
+ /** An exchange's LAN, for `circuit peeringdb`. */
28
+ exchange?: ExchangeLan;
29
+ };
30
+ /** A GRE tunnel to it. */
31
+ tunnel?: {
32
+ local: IP;
33
+ remote: IP;
34
+ addresses: readonly InterfaceAddress[];
35
+ mtu?: number;
36
+ };
37
+ /** Our BGP sessions with it, by name: its address, and ours when it is not the interface's. */
38
+ sessions: Readonly<Record<string, {
39
+ address: IP;
40
+ local_address?: IP;
41
+ }>>;
42
+ /** The sessions are more than one hop away, from this address of ours. They share a group. */
43
+ multihop?: {
44
+ local_address: IP;
45
+ families?: readonly Family[];
46
+ password?: Secret;
47
+ };
48
+ /** The most prefixes a session may send, by its family: about twice what it sends. Over it, the session closes until it is cleared. */
49
+ limits?: {
50
+ ipv4?: number;
51
+ ipv6?: number;
52
+ };
53
+ /** What we take. Nothing when absent. */
54
+ import?: {
55
+ /** Its default route, at this preference. */
56
+ default?: {
57
+ local_pref?: number;
58
+ description?: string;
59
+ };
60
+ /** Its routes, through `Bgp.learn`: the `before` rules, sanity, this preference, the tag. Only the default when absent. */
61
+ learn?: {
62
+ local_pref?: number;
63
+ before?: readonly PolicyRule<never, never>[];
64
+ };
65
+ };
66
+ /** Rules of yours in its export, between the community actions and what we announce, such as a prepend. */
67
+ export?: readonly PolicyRule<never, never>[];
68
+ /** What we announce to it. Everything else is rejected. */
69
+ announce: readonly Announced[];
70
+ /** Drop RFC 1918 sources arriving from it. `privateDrops()` lists the VLANs. */
71
+ drop_private?: true;
72
+ };
73
+ /** Upstreams by key. The key names everything built from it. */
74
+ export type UpstreamCatalog = Readonly<Record<string, Upstream>>;
75
+ /** How each upstream's policies are written: shared by every upstream of a catalog. */
76
+ export type UpstreamPolicies<P extends string> = {
77
+ /** A policy holding `Bgp.sanity()` that each import calls. The sanity rules are written in each import when absent. */
78
+ sanity?: P;
79
+ /** Where a route came from, by upstream: a community or a rule, such as `communityScheme(...).tag(...)`. Nothing is tagged when absent. */
80
+ tag?: (key: string, upstream: Upstream) => StandardCommunity | LargeCommunity | PolicyRule<never, never>;
81
+ /** Rules first in each export, such as `communityScheme(...).actions(upstream.asn)`. */
82
+ actions?: (key: string, upstream: Upstream) => readonly PolicyRule<never, never>[];
83
+ /** The source address for routes of its family, one per family. */
84
+ sources?: readonly IP[];
85
+ };
86
+ type Key<C> = keyof C & string;
87
+ /** The upstreams that land on a VLAN of ours. */
88
+ type Landed<C> = {
89
+ [K in Key<C>]: C[K] extends {
90
+ handoff: object;
91
+ } ? K : never;
92
+ }[Key<C>];
93
+ /** The upstreams that send their VLAN tagged. */
94
+ type Tagged<C> = {
95
+ [K in Key<C>]: C[K] extends {
96
+ handoff: {
97
+ native: number;
98
+ };
99
+ } ? K : never;
100
+ }[Key<C>];
101
+ /** The upstreams reached over a tunnel. */
102
+ type Tunneled<C> = {
103
+ [K in Key<C>]: C[K] extends {
104
+ tunnel: object;
105
+ } ? K : never;
106
+ }[Key<C>];
107
+ /** The upstreams whose sessions share a group: an exchange's route servers, or multihop sessions. */
108
+ type Grouped<C> = {
109
+ [K in Key<C>]: C[K] extends {
110
+ role: "rs";
111
+ } | {
112
+ multihop: object;
113
+ } ? K : never;
114
+ }[Key<C>];
115
+ /** The upstreams we take routes from. */
116
+ type Importing<C> = {
117
+ [K in Key<C>]: C[K] extends {
118
+ import: object;
119
+ } ? K : never;
120
+ }[Key<C>];
121
+ /** A native VLAN's name. */
122
+ type Native<K extends string> = `${K}-native`;
123
+ /** Each upstream's policies: `TRANSIT_A-IN` and `TRANSIT_A-OUT`. */
124
+ type PolicyName<C> = `${Uppercase<Importing<C>>}-IN` | `${Uppercase<Key<C>>}-OUT`;
125
+ /** The catalog as written, with any key the shape does not have refused, at every level. */
126
+ type Exact<S, T> = T extends readonly unknown[] | string | number | boolean | Secret ? T : {
127
+ [K in keyof T]: K extends keyof S ? Exact<NonNullable<S[K]>, T[K]> : never;
128
+ };
129
+ /** A network's upstreams written once, and each device's part built from them. */
130
+ export declare class Upstreams {
131
+ /**
132
+ * Each upstream written once: its AS, where it lands, its sessions, limits and policy. Everything a device needs is built from it, under names made from each key, so a device that names one is checked:
133
+ *
134
+ * - VLAN `<key>`, and `<key>-native` for one that sends its VLAN tagged. The router's interface is `<key>` too, a VLAN interface or a GRE tunnel.
135
+ * - Policies `<KEY>-IN` and `<KEY>-OUT`; a BGP group `<key>` for an exchange's route servers or multihop sessions.
136
+ *
137
+ * On the router: `vlans()`, `interfaces()`, `groups()`, `neighbors()`, `policies({ ... })`, `tunnelRules()`, `addresses()`. On a switch: `vlans(device)`, `ports(device)`, `vlansOn(device)`. For checks and ACLs: `untrusted()`, `natives()`, `privateDrops()`. Lists follow the catalog's order, where order counts on a device.
138
+ */
139
+ static define<const C extends UpstreamCatalog>(catalog: C & {
140
+ [K in keyof C]: Exact<Upstream, C[K]>;
141
+ }): {
142
+ vlans: <const D extends string | undefined = undefined>(device?: D) => D extends string ? { [K in Landed<C>]: Vlan; } & { [K in Tagged<C> as Native<K>]: Vlan; } : { [K in Landed<C>]: Vlan; };
143
+ vlansOn: (device: string) => Landed<C>[];
144
+ ports: (device: string) => Record<string, Port<`${Tagged<C>}-native` | Landed<C>, never, never, never>>;
145
+ interfaces: () => { [K in Landed<C>]: VlanInterface<Landed<C>, never>; } & { [K in Tunneled<C>]: Gre<never>; };
146
+ groups: () => Record<Grouped<C>, NeighborSettings<PolicyName<C>>>;
147
+ neighbors: () => Record<string, Neighbor<PolicyName<C>, Grouped<C>>>;
148
+ policies: <const P extends string = never>(options?: UpstreamPolicies<P>) => Record<PolicyName<C>, PolicyRule<P, never>[]>;
149
+ untrusted: () => (`${Tagged<C>}-native` | Landed<C>)[];
150
+ natives: () => `${Tagged<C>}-native`[];
151
+ privateDrops: () => Landed<C>[];
152
+ tunnelRules: () => FilterRule<never, never>[];
153
+ addresses: () => IP[];
154
+ };
155
+ }
156
+ export {};
@@ -1,8 +1,10 @@
1
- export { defineDevice, defineNetwork, secret, secretFile } from "./core/define";
2
- export { Utils } from "./core/utils";
3
- export type { Check, Community, Finding, NetBoxDevice, Network } from "./core/define";
4
- export type * from "./schema";
1
+ export { defineDevice, defineNetwork, secret, secretFile } from "./core/define.js";
2
+ export { Utils } from "./core/utils.js";
3
+ export type { Check, Community, Finding, NetBoxDevice, Network } from "./core/define.js";
4
+ export type * from "./schema/index.js";
5
+ /** The platforms, their models and each model's ports, for types of your own: `PortName<"junos", "QFX5100-48S-6Q">` is each port of that model, such as `"10g-1"` or `"40g-6"`. */
6
+ export type { ModelName, PlatformName, PortName } from "./platforms/catalog.js";
5
7
  /** Address helpers, for checks of your own. */
6
- export { Address } from "./core/address";
8
+ export { Address } from "./core/address.js";
7
9
  /** Secrets, for tools of your own: a secret's value, text with every secret hidden, and how Circuit logs in to a device. */
8
- export { Secrets } from "./core/secrets";
10
+ export { Secrets } from "./core/secrets.js";
@@ -1,5 +1,5 @@
1
- import type { Network } from "../core/define";
2
- import type { Device } from "../schema";
1
+ import type { Network } from "../core/define.js";
2
+ import type { Device } from "../schema/index.js";
3
3
  /** A device LibreNMS monitors whose settings differ from its config. */
4
4
  export type LibreNmsChange = {
5
5
  device: Device;
@@ -1,4 +1,4 @@
1
- import type { Network } from "../core/define";
1
+ import type { Network } from "../core/define.js";
2
2
  /** Each kind of object Circuit writes, in the order they are made: each refers only to kinds before it, or waits for the next pass. */
3
3
  export type NetBoxKind = "tag" | "site" | "manufacturer" | "platform" | "device-type" | "role" | "vrf" | "vlan" | "device" | "interface" | "prefix" | "ip-range" | "ip" | "cable";
4
4
  /** One object to make, change or delete. */
@@ -1,4 +1,4 @@
1
- import type { Network } from "../core/define";
1
+ import type { Network } from "../core/define.js";
2
2
  /** A change to send. `id` is set when the record exists and is updated in place. */
3
3
  export type PeeringDbChange = {
4
4
  summary: string;
@@ -1,5 +1,5 @@
1
- import type { Network } from "../core/define";
2
- import type { Device } from "../schema";
1
+ import type { Network } from "../core/define.js";
2
+ import type { Device } from "../schema/index.js";
3
3
  export type PrometheusTarget = {
4
4
  targets: string[];
5
5
  labels: Record<string, string>;
@@ -1,5 +1,5 @@
1
- import type { Family } from "../schema";
2
- import type { Registry } from "../schema/policy";
1
+ import type { Family } from "../schema/index.js";
2
+ import type { Registry } from "../schema/policy.js";
3
3
  export declare class Registries {
4
4
  /** The prefixes of one family a registry gives for a query. */
5
5
  static prefixes(name: Registry, query: string, family: Family): Promise<string[]>;
@@ -1,4 +1,4 @@
1
- import type { BgpSession } from "./platform";
1
+ import type { BgpSession } from "./platform.js";
2
2
  /** How long to wait, and how often to read again, both in seconds. */
3
3
  export type WaitLimits = {
4
4
  seconds: number;
@@ -15,4 +15,10 @@ export declare class BgpWait {
15
15
  static until(read: () => Promise<BgpSession[]>, enough: (sessions: BgpSession[]) => boolean, limits: WaitLimits, waiting?: (sessions: BgpSession[]) => void): Promise<BgpSession[]>;
16
16
  /** How many of these sessions are established. */
17
17
  static upCount(sessions: readonly BgpSession[]): number;
18
+ /** Sessions closed on their limit within this many minutes of each other are one burst. */
19
+ static readonly BURST_MINUTES = 5;
20
+ /**
21
+ * Groups of two or more sessions closed on their limit within `BURST_MINUTES` of each other, earliest first. Several limits at once is usually a leak upstream, not growth, so raising them is the wrong fix.
22
+ */
23
+ static limitBursts(sessions: readonly BgpSession[]): BgpSession[][];
18
24
  }
@@ -1,4 +1,4 @@
1
- import type { Device } from "../schema";
1
+ import type { Device } from "../schema/index.js";
2
2
  /** A feature a platform renders once proved on one of its devices. */
3
3
  export type ProvedFeature = "bfd-timers" | "static-route-bfd" | "multihop-bfd" | "alarm-severity" | "mac-limit" | "ethertype-filter";
4
4
  export declare class Derive {
@@ -1,5 +1,5 @@
1
- import type { AclRule, Device, Family, FilterMatch, FilterRule, NatRule, Ports, Protocol } from "../../schema";
2
- import { FortiOsBlock } from "./tree";
1
+ import type { AclRule, Device, Family, FilterMatch, FilterRule, NatRule, Ports, Protocol } from "../../schema/index.js";
2
+ import { FortiOsBlock } from "./tree.js";
3
3
  export declare class FortiOsFirewall {
4
4
  /** IP protocol numbers, for a protocol the platform names only by number. */
5
5
  static readonly PROTOCOL_NUMBER: Record<Protocol, number>;
@@ -1,7 +1,7 @@
1
- import type { Neighbor } from "../../core/cabling";
2
- import type { Device, Family } from "../../schema";
3
- import { type ConsoleDialect } from "../../transport/console";
4
- import { type ApplyOptions, type OsSupport, type Plan, type ReadOptions, type ReadSession, Platform } from "../platform";
1
+ import type { Neighbor } from "../../core/cabling.js";
2
+ import type { Device, Family } from "../../schema/index.js";
3
+ import { type ConsoleDialect } from "../../transport/console.js";
4
+ import { type ApplyOptions, type OsSupport, type Plan, type ReadOptions, type ReadSession, Platform } from "../platform.js";
5
5
  /** What a session reads and writes: an SSH shell, or anything else that streams the CLI. */
6
6
  export type Channel = {
7
7
  write(data: string): unknown;
@@ -1,4 +1,4 @@
1
- import type { Device, Family } from "../../schema";
1
+ import type { Device, Family } from "../../schema/index.js";
2
2
  type Addressed = {
3
3
  addresses?: readonly (string | {
4
4
  address: string;
@@ -1,4 +1,4 @@
1
- import type { ApplyOptions, Plan } from "../platform";
1
+ import type { ApplyOptions, Plan } from "../platform.js";
2
2
  export declare class FortiOsPlan {
3
3
  /** Objects every FortiGate running 6.2 is shipped with, which policies and profiles of the factory name. */
4
4
  private static readonly FACTORY_ADDRESSES;
@@ -1,4 +1,4 @@
1
- import type { Device, Helper, Severity } from "../../schema";
1
+ import type { Device, Helper, Severity } from "../../schema/index.js";
2
2
  export type Section = (typeof FortiOsRender.SECTIONS)[number];
3
3
  export declare class FortiOsRender {
4
4
  /** Where the pre-login banner's text is kept. */
@@ -1,5 +1,5 @@
1
- import type { Device, Family, Neighbor, NeighborSettings, PolicyRule } from "../../schema";
2
- import { FortiOsBlock } from "./tree";
1
+ import type { Device, Family, Neighbor, NeighborSettings, PolicyRule } from "../../schema/index.js";
2
+ import { FortiOsBlock } from "./tree.js";
3
3
  type AnyRule = PolicyRule<string, string>;
4
4
  type AnyNeighbor = Neighbor<string, string>;
5
5
  /** The lengths a prefix list's rule takes within its prefix. */
@@ -1,4 +1,4 @@
1
- import type { Platform } from "./platform";
1
+ import type { Platform } from "./platform.js";
2
2
  /** Every platform the framework can render and apply, by name. */
3
3
  export declare class Platforms {
4
4
  private static readonly BY_NAME;
@@ -1,5 +1,5 @@
1
- import type { Device, Neighbor, NeighborSettings } from "../../schema";
2
- import { type JunosStatements } from "./render";
1
+ import type { Device, Neighbor, NeighborSettings } from "../../schema/index.js";
2
+ import { type JunosStatements } from "./render.js";
3
3
  type AnyNeighbor = Neighbor<string, string>;
4
4
  /** BGP sessions, the policies they use, prefix sets and RPKI servers, added to one device's statements. */
5
5
  export declare class JunosBgp {
@@ -1,7 +1,7 @@
1
- import type { Neighbor } from "../../core/cabling";
2
- import type { Device, Family } from "../../schema";
3
- import { type ConsoleDialect } from "../../transport/console";
4
- import { type ApplyOptions, type OsSupport, type Plan, type ReadOptions, type ReadSession, Platform } from "../platform";
1
+ import type { Neighbor } from "../../core/cabling.js";
2
+ import type { Device, Family } from "../../schema/index.js";
3
+ import { type ConsoleDialect } from "../../transport/console.js";
4
+ import { type ApplyOptions, type OsSupport, type Plan, type ReadOptions, type ReadSession, Platform } from "../platform.js";
5
5
  /** How a commit that could cut access is guarded: it rolls back after `minutes` unless `prove` shows access survived. */
6
6
  export type Guard = {
7
7
  minutes: number;
@@ -1,4 +1,4 @@
1
- import type { Device, Interface, Port, Vrrp } from "../../schema";
1
+ import type { Device, Interface, Port, Vrrp } from "../../schema/index.js";
2
2
  type AnyPort = Port<string, string, string, string>;
3
3
  type AnyInterface = Interface<string, string>;
4
4
  type LagInterface = Extract<AnyInterface, {