@takodotid/circuit 0.8.0 → 0.10.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 (51) hide show
  1. package/README.md +3 -2
  2. package/dist/chunks/index-ex1wcst7.js +109 -0
  3. package/dist/chunks/index-fq57hya4.js +20 -0
  4. package/dist/chunks/new-7n8qvtjd.js +168 -0
  5. package/dist/cli/index.js +4079 -0
  6. package/dist/index.js +48 -0
  7. package/dist/presets/index.js +257 -0
  8. package/dist/types/adapters/devices/catalog.d.ts +58 -0
  9. package/dist/types/adapters/devices/derive.d.ts +5 -0
  10. package/dist/types/adapters/devices/index.d.ts +3 -0
  11. package/dist/types/adapters/devices/lines.d.ts +56 -0
  12. package/dist/types/adapters/devices/raisecom-ros/index.d.ts +23 -0
  13. package/dist/types/adapters/devices/raisecom-ros/models.d.ts +14 -0
  14. package/dist/types/adapters/devices/routeros/export.d.ts +39 -0
  15. package/dist/types/adapters/devices/routeros/index.d.ts +17 -0
  16. package/dist/types/adapters/devices/routeros/models.d.ts +14 -0
  17. package/dist/types/adapters/devices/routeros/plan.d.ts +4 -0
  18. package/dist/types/adapters/devices/routeros/render.d.ts +96 -0
  19. package/dist/types/adapters/devices/types.d.ts +34 -0
  20. package/dist/types/adapters/devices/vrp/index.d.ts +24 -0
  21. package/dist/types/adapters/devices/vrp/models.d.ts +12 -0
  22. package/dist/types/adapters/registries/index.d.ts +3 -0
  23. package/dist/types/adapters/registries/peeringdb.d.ts +16 -0
  24. package/dist/types/core/addr.d.ts +24 -0
  25. package/dist/types/core/define.d.ts +50 -0
  26. package/dist/types/core/merge.d.ts +4 -0
  27. package/dist/types/core/secrets.d.ts +21 -0
  28. package/dist/types/core/time.d.ts +2 -0
  29. package/dist/types/core/validate.d.ts +2 -0
  30. package/dist/types/index.d.ts +6 -0
  31. package/dist/types/presets/index.d.ts +99 -0
  32. package/dist/types/schema/common.d.ts +24 -0
  33. package/dist/types/schema/device.d.ts +80 -0
  34. package/dist/types/schema/firewall.d.ts +118 -0
  35. package/dist/types/schema/index.d.ts +7 -0
  36. package/dist/types/schema/interfaces.d.ts +181 -0
  37. package/dist/types/schema/policy.d.ts +88 -0
  38. package/dist/types/schema/routing.d.ts +115 -0
  39. package/dist/types/schema/system.d.ts +186 -0
  40. package/dist/types/transport/ssh.d.ts +22 -0
  41. package/package.json +19 -5
  42. package/src/adapters/devices/routeros/TRAPS.md +2 -0
  43. package/src/adapters/devices/routeros/render.ts +1 -0
  44. package/src/cli/index.ts +10 -2
  45. package/src/cli/new.ts +60 -12
  46. package/src/core/secrets.ts +7 -9
  47. package/src/presets/index.ts +27 -1
  48. package/src/schema/policy.ts +2 -0
  49. package/templates/project/AGENTS.md +10 -10
  50. package/templates/project/README.md +7 -7
  51. package/templates/project/tsconfig.json +1 -1
@@ -0,0 +1,34 @@
1
+ import type { Device, Family } from "../../schema";
2
+ /** One part of a plan: what a reviewer reads, and what is sent. */
3
+ export type Step = {
4
+ title: string;
5
+ show: string[];
6
+ send: string[];
7
+ };
8
+ /** The commands that turn what a device runs into what its config says. `problems` blocks an apply: something the adapter cannot converge. */
9
+ export type Plan = {
10
+ steps: Step[];
11
+ problems: string[];
12
+ };
13
+ export type ApplyOptions = {
14
+ /** Also send every secret, so a rotated value reaches the device. Secrets cannot be read back, so they are otherwise only sent when the object holding them is new. */
15
+ secrets: boolean;
16
+ /** Minutes before a platform that can restore itself does so, if the run does not prove access survived. */
17
+ rollback: number;
18
+ };
19
+ export type DeviceAdapter = {
20
+ /** File extension of a saved snapshot, for syntax highlighting. */
21
+ extension: string;
22
+ /** Fields this platform cannot express. Validation fails on any of them, so nothing is skipped silently. */
23
+ unsupported(device: Device): string[];
24
+ /** The whole configuration the device should run. Secrets appear as markers, never values. */
25
+ render(device: Device): string;
26
+ /** What the device runs now, secrets removed, in the form `render` produces where the platform allows. */
27
+ read(device: Device, network: readonly Device[]): Promise<string>;
28
+ /** What it takes to go from `current` to `desired`. */
29
+ plan(desired: string, current: string, options: ApplyOptions): Plan;
30
+ /** Replace the contents of a prefix set fetched from a registry. Absent on a platform that cannot hold one. */
31
+ fill?(device: Device, network: readonly Device[], set: string, family: Family, prefixes: string[]): Promise<void>;
32
+ /** Send a plan, keeping the device reachable or restoring it. */
33
+ apply(device: Device, network: readonly Device[], plan: Plan, options: ApplyOptions): Promise<void>;
34
+ };
@@ -0,0 +1,24 @@
1
+ import type { Client, ClientChannel } from "ssh2";
2
+ import type { Device } from "../../../schema";
3
+ import { type Dialect } from "../lines";
4
+ import type { DeviceAdapter, Plan } from "../types";
5
+ export declare const dialect: Dialect;
6
+ /** The ACL that guards every management service. */
7
+ export declare const MANAGEMENT_ACL = 2000;
8
+ /** `99 999 to 1000 2000`, the platform's compression of a VLAN list. */
9
+ /** The VLAN IDs of a `vlan batch` line: `vlan batch 10 20 to 30`. */
10
+ export declare function batchIds(line: string): number[];
11
+ export declare function vlanBatch(ids: number[]): string;
12
+ export declare function render(device: Device): string;
13
+ export declare function login(device: Device): Promise<Client>;
14
+ /**
15
+ * An interactive shell at the user prompt, paging off.
16
+ *
17
+ * Every login asks whether to change the password. Answering yes replaces it at once and every stored credential with it, so the answer is always no. Anything sent before the question is answered is taken as the answer.
18
+ */
19
+ export declare function openShell(client: Client): Promise<ClientChannel>;
20
+ /** What the device runs, credentials scrubbed. */
21
+ export declare function read(device: Device): Promise<string>;
22
+ export declare function unsupported(device: Device): string[];
23
+ export declare function apply(device: Device, _network: readonly Device[], plan: Plan): Promise<void>;
24
+ export declare const vrp: DeviceAdapter;
@@ -0,0 +1,12 @@
1
+ export declare const models: {
2
+ readonly "CE6855-48S6Q-HI": {
3
+ readonly ports: {
4
+ readonly "10g": 48;
5
+ readonly "40g": 6;
6
+ };
7
+ readonly spelling: {
8
+ readonly "10g": "10GE1/0/{n}";
9
+ readonly "40g": "40GE1/0/{n}";
10
+ };
11
+ };
12
+ };
@@ -0,0 +1,3 @@
1
+ import type { Family } from "../../schema";
2
+ import type { Registry } from "../../schema/policy";
3
+ export declare const registries: Record<Registry, (query: string, family: Family) => Promise<string[]>>;
@@ -0,0 +1,16 @@
1
+ import type { Network } from "../../core/define";
2
+ /** A change to send. `id` is set when the record exists and is updated in place. */
3
+ export type PeeringDbChange = {
4
+ summary: string;
5
+ id?: number;
6
+ body: Record<string, unknown>;
7
+ };
8
+ export type PeeringDbPlan = {
9
+ changes: PeeringDbChange[];
10
+ /** Records PeeringDB holds that the config does not. */
11
+ unknown: string[];
12
+ };
13
+ /** What would change at PeeringDB. Reads the public API; needs no key. */
14
+ export declare function planPeeringDb(network: Network): Promise<PeeringDbPlan>;
15
+ /** Send the changes, with the network's API key. */
16
+ export declare function sendPeeringDb(network: Network, changes: PeeringDbChange[]): Promise<void>;
@@ -0,0 +1,24 @@
1
+ import type { Family } from "../schema";
2
+ export declare const familyOf: (address: string) => Family;
3
+ /** An address as a number, or null when it is not one. */
4
+ export declare function parse(address: string): bigint | null;
5
+ /** `10.0.0.1/24` to its parts. Throws when it is not an address with a length. */
6
+ export declare function split(cidr: string): {
7
+ addr: string;
8
+ len: number;
9
+ family: Family;
10
+ };
11
+ export declare const isAddress: (text: string) => boolean;
12
+ export declare function isCidr(text: string): boolean;
13
+ /** `10.0.0.1/24` to `10.0.0.0/24`. */
14
+ export declare function network(cidr: string): string;
15
+ /** Is the address or prefix `inner` inside `outer`? */
16
+ export declare function contains(outer: string, inner: string): boolean;
17
+ /** `/25` to `255.255.255.128`. */
18
+ export declare const netmask: (length: number) => string;
19
+ /** `/24` to `0.0.0.255`, the inverted mask some ACLs take. */
20
+ export declare const wildcard: (length: number) => string;
21
+ /** The address with its length dropped. */
22
+ export declare const host: (cidr: string) => string;
23
+ /** Whether another network can reach the address. IPv6 is global only inside 2000::/3, less documentation space. */
24
+ export declare function isGlobal(address: string): boolean;
@@ -0,0 +1,50 @@
1
+ import type { ModelName, PlatformName, PortName } from "../adapters/devices/catalog";
2
+ import type { Device, Secret } from "../schema";
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
+ export declare const secret: (name: string) => Secret;
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. */
6
+ export declare const secretFile: (path: string) => Secret;
7
+ /**
8
+ * Declare one device.
9
+ *
10
+ * The names it declares (VLANs, interfaces, policies and the rest) are the only names its other fields accept, and the editor suggests them. The port names come from the model.
11
+ */
12
+ export declare function defineDevice<const Platform extends PlatformName, const Model extends ModelName<Platform>, const Vlans extends string = never, const Interfaces extends string = never, const Policies extends string = never, const PrefixSets extends string = never, const BgpGroups extends string = never, const Acls extends string = never, const AddressSets extends string = never, const Users extends string = never, const Vrfs extends string = never, const Certificates extends string = never>(device: Device<Platform, Model, PortName<Platform, Model>, Vlans, Interfaces, Policies, PrefixSets, BgpGroups, Acls, AddressSets, Users, Vrfs, Certificates>): Device;
13
+ /** Something a check found. */
14
+ export type Finding = {
15
+ /** An error stops `diff` and `apply`; a warning is reported only. */
16
+ level: "error" | "warning";
17
+ /** The device it is about, when it is about one. */
18
+ device?: string;
19
+ /** What is wrong, in a sentence. */
20
+ message: string;
21
+ };
22
+ /** A rule of your own design, run by `validate` beside the framework's. */
23
+ export type Check = (devices: readonly Device[]) => readonly Finding[];
24
+ /**
25
+ * One BGP community the network defines, in the community description format of the NLNOG Ring looking glass, which bgp.tools also reads.
26
+ *
27
+ * `community` takes `nnn` for any number, `x` for one digit and `a-b` for a range, such as `65535:0:nnn`. `description` refers to what each wildcard matched as `$0`, `$1` and so on, such as `do not announce to AS$0`.
28
+ */
29
+ export type Community = {
30
+ community: string;
31
+ description: string;
32
+ };
33
+ /** The whole network. */
34
+ export type Network = {
35
+ /** Every device the framework manages. */
36
+ devices: readonly Device[];
37
+ /** Rules of your own design. */
38
+ checks?: readonly Check[];
39
+ /** The AS the network operates, for what is published about it. */
40
+ asn?: number;
41
+ /** The BGP communities the network defines, printed by `circuit communities` for a looking glass or bgp.tools. */
42
+ communities?: readonly Community[];
43
+ /** Keeps the network's PeeringDB record in step with the config: `circuit peeringdb`. Needs `asn`. */
44
+ peeringdb?: {
45
+ /** An API key with write access to the network's record. */
46
+ api_key: Secret;
47
+ };
48
+ };
49
+ /** Declare the network, in `circuit.config.ts`. */
50
+ export declare const defineNetwork: (network: Network) => Network;
@@ -0,0 +1,4 @@
1
+ type Merged<T extends readonly object[]> = T extends readonly [infer Head, ...infer Tail extends readonly object[]] ? Head & Merged<Tail> : unknown;
2
+ /** Combine records into one. Unlike spreading them, a name two of them hold is an error rather than one silently replacing the other. */
3
+ export declare function merge<const T extends readonly Record<string, unknown>[]>(...records: T): Merged<T>;
4
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { Secret } from "../schema";
2
+ /** Find `.env` and secret files from this directory, the config file's, instead of the working directory. */
3
+ export declare function useRoot(directory: string): void;
4
+ export declare const isSecret: (value: unknown) => value is Secret;
5
+ /** How a reference is written inside a marker: a variable's name, or `file:` and a path. */
6
+ export declare const referenceOf: (secret: Secret) => string;
7
+ /** Where a reference's value comes from, such as `.env` or `1Password, through .env`, without reading it. Undefined when it is set nowhere. */
8
+ export declare function sourceOf(reference: string): string | undefined;
9
+ /** The value behind a reference, as `referenceOf` writes it. */
10
+ declare function valueOf(reference: string): string;
11
+ /** The value of a secret, or of a variable named directly. */
12
+ export declare const resolve: (secret: Secret | string) => string;
13
+ /** How a secret appears in rendered config. Replaced with its value only when sent to a device. */
14
+ export declare const marker: (secret: Secret) => string;
15
+ /** A marker anywhere in a command. Its group is the reference. */
16
+ export declare const MARKER: RegExp;
17
+ /** Replace every marker in a command with its value. */
18
+ export declare const substitute: (text: string) => string;
19
+ /** The value behind one marker's reference, for a platform that takes a secret interactively. */
20
+ export declare const valueOfReference: typeof valueOf;
21
+ export {};
@@ -0,0 +1,2 @@
1
+ /** Minutes east of UTC an IANA zone is at now, for platforms that take an offset rather than a zone. */
2
+ export declare function offset(zone: string): number;
@@ -0,0 +1,2 @@
1
+ import type { Finding, Network } from "./define";
2
+ export declare function validate(network: Network): Finding[];
@@ -0,0 +1,6 @@
1
+ export { defineDevice, defineNetwork, secret, secretFile } from "./core/define";
2
+ export { merge } from "./core/merge";
3
+ export type { Check, Community, Finding, Network } from "./core/define";
4
+ export type * from "./schema";
5
+ /** Address helpers, for checks of your own. */
6
+ export { contains, familyOf, host, isAddress, isCidr, isGlobal, network as prefixOf } from "./core/addr";
@@ -0,0 +1,99 @@
1
+ import type { Check, Community } from "../core/define";
2
+ import type { AclRule, Device, FilterRule, PolicyRule, Prefix } from "../schema";
3
+ /** IPv4 sources that never arrive from the internet: loopback, link-local and "this network", after RFC 6890. */
4
+ export declare const MARTIANS: readonly ["127.0.0.0/8", "169.254.0.0/16", "0.0.0.0/8"];
5
+ /** IPv4 private space, RFC 1918. */
6
+ export declare const PRIVATE_RANGES: readonly ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"];
7
+ /** Hardware ACL rules dropping traffic arriving on a VLAN from sources that cannot be there: your own space, martians, private ranges. One rule per source. */
8
+ export declare const antiSpoofing: <V extends string>(vlan: V, sources: readonly Prefix[]) => AclRule<V>[];
9
+ /** Firewall rules dropping packets with a TCP flag combination no legitimate packet carries, for a forward chain. */
10
+ export declare const badTcpFlags: () => FilterRule<never, never>[];
11
+ /** The prefix lengths a BGP import accepts. */
12
+ export type SanityLengths = {
13
+ /** Shortest and longest IPv4 prefix accepted. /8 to /24 when absent. */
14
+ ipv4?: {
15
+ min: number;
16
+ max: number;
17
+ };
18
+ /** Shortest and longest IPv6 prefix accepted. /16 to /48 when absent. */
19
+ ipv6?: {
20
+ min: number;
21
+ max: number;
22
+ };
23
+ };
24
+ /** 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. */
25
+ export declare function bgpSanity(lengths?: SanityLengths): PolicyRule<never, never>[];
26
+ /** Each BGP neighbor's own policies, or its group's. */
27
+ export declare function neighborPolicies(device: Device): {
28
+ name: string;
29
+ address: string;
30
+ import: string | undefined;
31
+ export: string | undefined;
32
+ }[];
33
+ /** The prefixes an export policy accepts by name. */
34
+ export declare function offered(device: Device, policy: string | undefined): string[];
35
+ /** Every BGP export ends in a reject that matches everything, so nothing leaves by accident. */
36
+ export declare const exportsEndInReject: Check;
37
+ /** The VLANs a port carries, untagged or tagged. */
38
+ export declare const vlansOf: (port: {
39
+ access_vlan?: string;
40
+ trunk_vlans?: readonly string[];
41
+ }) => string[];
42
+ /** 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. */
43
+ export type TrustBoundary = {
44
+ /** VLANs carrying traffic straight from outside: transits, exchanges, an ISP handoff. */
45
+ untrusted: readonly string[];
46
+ /** The devices that filter: the only ones that may give an untrusted VLAN an address, and the only ones a port carrying both kinds may face. */
47
+ routers: readonly string[];
48
+ };
49
+ /** A check: traffic from outside reaches your own VLANs only through a router. A port or LAG carrying an untrusted VLAN beside any other is on a router or faces one, and only a router gives an untrusted VLAN an address. */
50
+ export declare const trustBoundary: (boundary: TrustBoundary) => Check;
51
+ /** A GRE or VXLAN tunnel's local address is outside what the neighbors over it are offered, or their replies route back into the tunnel. */
52
+ export declare const tunnelsOutsideOffered: Check;
53
+ /**
54
+ * A BGP community scheme of large communities, `asn:function:parameter`, in the style of RFC 8195. Each field names the function number your network uses for it; leave out what your network does not offer.
55
+ */
56
+ export type CommunityScheme<Class extends string = string> = {
57
+ /** Your AS. It is the first part of every community. */
58
+ asn: number;
59
+ /** Set on import: the kind of neighbor a route came from, `asn:function:class`. */
60
+ learned_from?: {
61
+ function: number;
62
+ classes: Readonly<Record<Class, number>>;
63
+ };
64
+ /** Set on import: the site a route was learned at, `asn:function:site`. `sites` names the sites, for the catalogue. */
65
+ learned_at?: {
66
+ function: number;
67
+ sites?: Readonly<Record<number, string>>;
68
+ };
69
+ /** Set on import: the AS a route was learned from, `asn:function:asn`. */
70
+ learned_from_as?: number;
71
+ /** Set by a customer: do not announce the route to an AS, `asn:function:asn`, or to anyone, `asn:function:0`. */
72
+ do_not_announce?: number;
73
+ /** Set by a customer: prepend toward an AS once, twice or three times, `asn:function:asn`. */
74
+ prepend?: {
75
+ once?: number;
76
+ twice?: number;
77
+ three_times?: number;
78
+ };
79
+ /** Set by a customer: drop traffic to the route in your network, before it reaches them, `asn:function:0`. The route is never announced further. */
80
+ blackhole?: number;
81
+ };
82
+ /**
83
+ * Policy rules and a catalogue from one community scheme, so the communities your routers set, the ones they act on, and the ones you publish never disagree.
84
+ *
85
+ * - `tag(neighborClass, neighborAs, { site, keepActions })`: a rule for an import policy. It removes your communities a neighbor set, then adds where the route was learned. `site` is needed when the scheme has `learned_at`. With `keepActions`, for a customer, its action communities stay.
86
+ * - `actions(neighborAs)`: rules for an export policy toward one neighbor. They act on a customer's action communities, then remove all of yours. Place them before the rules that accept.
87
+ * - `blackhole(prefixSet)`: a rule for a customer's import policy, before anything that rejects long prefixes. It takes a route the customer asked to blackhole, but only inside `prefixSet`, its own space, and drops traffic to it here.
88
+ * - `catalogue`: every community, for `communities` in `defineNetwork`.
89
+ */
90
+ export declare function communityScheme<const Class extends string>(scheme: CommunityScheme<Class>): {
91
+ community: (fn: number, parameter: number | string) => string;
92
+ tag: (neighborClass: Class, neighborAs: number, options?: {
93
+ site?: number;
94
+ keepActions?: boolean;
95
+ }) => PolicyRule<never, never>;
96
+ actions: (neighborAs: number) => PolicyRule<never, never>[];
97
+ blackhole: <S extends string>(prefixSet: S) => PolicyRule<never, S>;
98
+ catalogue: Community[];
99
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A value kept out of the repository and resolved only when a device is applied.
3
+ *
4
+ * `secret("NAME")` reads a variable from the environment or `.env` beside the config; a value written as `op://vault/item/field` is read from 1Password. `secretFile("path")` reads a file, relative to the config file, for a value that spans lines such as a private key in PEM. Keep that file out of git.
5
+ */
6
+ export type Secret = {
7
+ /** Name of the value in the environment or in `.env`. */
8
+ readonly secret: string;
9
+ } | {
10
+ /** Path of a file holding the value, relative to the config file. */
11
+ readonly secret_file: string;
12
+ };
13
+ /** An address without a length, IPv4 or IPv6, for example `10.0.0.1`. */
14
+ export type IP = string;
15
+ /** A network with its length, for example `10.0.0.0/24`. */
16
+ export type Prefix = string;
17
+ /** An interface address with its length, for example `10.0.0.1/24`. */
18
+ export type Address = string;
19
+ /** An address family. */
20
+ export type Family = "ipv4" | "ipv6";
21
+ /** Bits per second, for example `2G`, `500M` or `64k`. */
22
+ export type Rate = `${number}${"k" | "M" | "G"}`;
23
+ /** A log severity, most severe first. A threshold includes everything above it. */
24
+ export type Severity = "emergency" | "alert" | "critical" | "error" | "warning" | "notice" | "info" | "debug";
@@ -0,0 +1,80 @@
1
+ import type { IP } from "./common";
2
+ import type { AclRule, Firewall } from "./firewall";
3
+ import type { Interface, Port, Vlan } from "./interfaces";
4
+ import type { PrefixSet, RoutePolicy } from "./policy";
5
+ import type { Routing } from "./routing";
6
+ import type { Certificate, DhcpRelay, DhcpServer, FlowExport, Management, System, User } from "./system";
7
+ /** How the framework reaches the device. */
8
+ export type Connection<U extends string> = {
9
+ /** Management address. */
10
+ host: IP;
11
+ /** One of the device's `users`. Its password is the login. */
12
+ user: U;
13
+ /** SSH port. 22 when absent. */
14
+ port?: number;
15
+ };
16
+ /** A separate routing table, with the interfaces that route in it. */
17
+ export type Vrf = {
18
+ /** What the VRF is for. */
19
+ description?: string;
20
+ };
21
+ /**
22
+ * Everything one device runs. What is not written here is removed from the device or returned to its default.
23
+ *
24
+ * Type parameters are the names the device declares, so a reference to a name that does not exist fails to compile: N ports, V VLANs, I interfaces, P policies, S prefix sets, G BGP groups, A ACLs, AS address sets, U users, F VRFs, C certificates.
25
+ */
26
+ export type Device<Pl extends string = string, M extends string = string, N extends string = string, V extends string = string, I extends string = string, P extends string = string, S extends string = string, G extends string = string, A extends string = string, AS extends string = string, U extends string = string, F extends string = string, C extends string = string> = {
27
+ /** Hostname. */
28
+ name: string;
29
+ /** Operating system, which decides the adapter that renders and applies the device. */
30
+ platform: Pl;
31
+ /** Hardware model. Decides which port names exist. */
32
+ model: M;
33
+ /** How the framework logs in. */
34
+ connection: Connection<NoInfer<U>>;
35
+ /** Device-wide settings. */
36
+ system?: System;
37
+ /** Logins, keyed by user name. Users not listed are removed. */
38
+ users?: Record<U, User>;
39
+ /** Ways in, and who may use them. */
40
+ management?: Management<NoInfer<I | N>, NoInfer<C>>;
41
+ /** Certificates, keyed by name. */
42
+ certificates?: Record<C, Certificate>;
43
+ /** VLANs, keyed by name. */
44
+ vlans?: Record<V, Vlan>;
45
+ /** Physical ports, keyed by `<speed>-<position>`. */
46
+ ports?: Partial<Record<N, Port<NoInfer<V>, NoInfer<A>, NoInfer<I>, NoInfer<F>>>>;
47
+ /** Interfaces that are not physical ports, keyed by name. */
48
+ interfaces?: Record<I, Interface<NoInfer<V>, NoInfer<F>>>;
49
+ /** Separate routing tables, keyed by name. */
50
+ vrfs?: Record<F, Vrf>;
51
+ /** Static routes, BGP, OSPF and route validation. */
52
+ routing?: Routing<NoInfer<P>, G, NoInfer<I | N>, NoInfer<F>>;
53
+ /** Route policies, keyed by name. */
54
+ policies?: Record<P, RoutePolicy<NoInfer<P>, NoInfer<S>>>;
55
+ /** Prefix sets that policies match on, keyed by name. */
56
+ prefix_sets?: Record<S, PrefixSet>;
57
+ /** Packet filtering and address translation. */
58
+ firewall?: Firewall<NoInfer<I | N>, AS>;
59
+ /** Hardware ACLs, keyed by name, applied to a port with `acl`. */
60
+ acls?: Record<A, readonly AclRule<NoInfer<V>>[]>;
61
+ /** DHCP servers, keyed by name. */
62
+ dhcp?: Record<string, DhcpServer<NoInfer<I | N>>>;
63
+ /** DHCP relays, keyed by name. */
64
+ dhcp_relay?: Record<string, DhcpRelay<NoInfer<I | N>>>;
65
+ /** Flow records or packet samples sent to collectors. */
66
+ flow_export?: FlowExport<NoInfer<I | N>>;
67
+ /** Advertise and learn neighbors with LLDP on every port, or only on the interfaces listed. A port may override it. */
68
+ lldp?: true | {
69
+ interfaces: readonly NoInfer<I | N>[];
70
+ };
71
+ /** Spanning tree on every switched port. A port may override it. */
72
+ stp?: {
73
+ /** Which variant runs. RSTP when absent. */
74
+ mode?: "stp" | "rstp" | "mstp";
75
+ /** Bridge priority, 0 to 61440 in steps of 4096. Lower wins the root election. */
76
+ priority?: number;
77
+ };
78
+ /** Forward in hardware where the platform can. An interface may override it. */
79
+ hardware_offload?: boolean;
80
+ };
@@ -0,0 +1,118 @@
1
+ import type { Family, IP, Prefix, Rate } from "./common";
2
+ /** An IP protocol. `icmp` means ICMPv6 in an IPv6 rule. */
3
+ export type Protocol = "tcp" | "udp" | "icmp" | "gre" | "esp" | "ah" | "ospf" | "vrrp";
4
+ /** The state connection tracking assigns a packet. */
5
+ export type ConnectionState = "new" | "established" | "related" | "untracked" | "invalid";
6
+ /** A TCP header flag. */
7
+ export type TcpFlag = "fin" | "syn" | "rst" | "psh" | "ack" | "urg";
8
+ /** Conditions on a packet. All must hold. A rule with addresses of one family applies to that family only. */
9
+ export type FilterMatch<I extends string, AS extends string> = {
10
+ /** Only packets of this family. */
11
+ family?: Family;
12
+ /** IP protocol. */
13
+ protocol?: Protocol;
14
+ /** Source address or prefix. */
15
+ src?: Prefix;
16
+ /** Destination address or prefix. */
17
+ dst?: Prefix;
18
+ /** Source is in this address set. */
19
+ src_set?: AS;
20
+ /** Destination is in this address set. */
21
+ dst_set?: AS;
22
+ /** Source port, or any of several. TCP and UDP only. */
23
+ src_port?: number | readonly number[];
24
+ /** Destination port, or any of several. TCP and UDP only. */
25
+ dst_port?: number | readonly number[];
26
+ /** Arrived on this interface. */
27
+ in_interface?: I;
28
+ /** Leaves through this interface. */
29
+ out_interface?: I;
30
+ /** Connection tracking state, any of these. */
31
+ state?: readonly ConnectionState[];
32
+ /** TCP flags that must be set, and flags that must not be. */
33
+ tcp_flags?: {
34
+ set?: readonly TcpFlag[];
35
+ unset?: readonly TcpFlag[];
36
+ };
37
+ };
38
+ /** One firewall rule. */
39
+ export type FilterRule<I extends string, AS extends string> = {
40
+ /** Shown beside the rule on the device. */
41
+ description?: string;
42
+ /** Which packets the rule applies to. Every packet when absent. */
43
+ match?: FilterMatch<I, AS>;
44
+ /** What happens to a matching packet. `reject` answers the sender, `drop` does not. */
45
+ action: "accept" | "drop" | "reject";
46
+ /** Hand the rest of an accepted flow to the fast path, past the remaining rules. */
47
+ offload?: true;
48
+ };
49
+ /** Rules evaluated in order, first match wins, then `default`. */
50
+ export type Chain<I extends string, AS extends string> = {
51
+ /** What happens to a packet no rule matched. Accept when absent. */
52
+ default?: "accept" | "drop";
53
+ /** Evaluated in order. */
54
+ rules?: readonly FilterRule<I, AS>[];
55
+ };
56
+ /** One address translation. */
57
+ export type NatRule<I extends string, AS extends string> = {
58
+ /** Shown beside the rule on the device. */
59
+ description?: string;
60
+ /** Which packets are translated. Every packet when absent. */
61
+ match?: FilterMatch<I, AS>;
62
+ } & ({
63
+ /** Use the address of the outgoing interface. */
64
+ action: "masquerade";
65
+ } | {
66
+ /** `snat` rewrites the source, `dnat` the destination. */
67
+ action: "snat" | "dnat";
68
+ /** The address to rewrite to. */
69
+ to: IP;
70
+ /** The port to rewrite to. Unchanged when absent. */
71
+ to_port?: number;
72
+ });
73
+ /** Connection-tracking helpers that rewrite application payloads. None run unless listed. */
74
+ export type Helper = "ftp" | "tftp" | "sip" | "h323" | "pptp" | "rtsp" | "irc";
75
+ /** What the device filters and translates. */
76
+ export type Firewall<I extends string, AS extends string> = {
77
+ /** Named groups of addresses and prefixes, of either family, that rules match on. */
78
+ address_sets?: Record<AS, readonly (IP | Prefix)[]>;
79
+ /** Traffic to the device, through it, and from it. */
80
+ filter?: {
81
+ input?: Chain<I, NoInfer<AS>>;
82
+ forward?: Chain<I, NoInfer<AS>>;
83
+ output?: Chain<I, NoInfer<AS>>;
84
+ };
85
+ /** Source translation on the way out, destination translation on the way in. */
86
+ nat?: {
87
+ source?: readonly NatRule<I, NoInfer<AS>>[];
88
+ destination?: readonly NatRule<I, NoInfer<AS>>[];
89
+ };
90
+ /** Helpers that run. None when absent. */
91
+ helpers?: readonly Helper[];
92
+ };
93
+ /** A stateless rule evaluated in hardware where traffic arrives, before routing. */
94
+ export type AclRule<V extends string> = {
95
+ /** Shown beside the rule on the device. */
96
+ description?: string;
97
+ /** Which packets the rule applies to. Every packet when absent. */
98
+ match?: {
99
+ /** Only packets of this family. */
100
+ family?: Family;
101
+ /** IP protocol. */
102
+ protocol?: Protocol;
103
+ /** Source prefix. */
104
+ src?: Prefix;
105
+ /** Destination prefix. */
106
+ dst?: Prefix;
107
+ /** Source port, or any of several. */
108
+ src_port?: number | readonly number[];
109
+ /** Destination port, or any of several. */
110
+ dst_port?: number | readonly number[];
111
+ /** Carried in this VLAN. */
112
+ vlan?: V;
113
+ };
114
+ /** What happens to a matching packet. */
115
+ action: "accept" | "drop";
116
+ /** Accepted traffic above this rate is dropped. */
117
+ rate?: Rate;
118
+ };
@@ -0,0 +1,7 @@
1
+ export type * from "./common";
2
+ export type * from "./device";
3
+ export type * from "./firewall";
4
+ export type * from "./interfaces";
5
+ export type * from "./policy";
6
+ export type * from "./routing";
7
+ export type * from "./system";