Skip to content

Policy file reference

The policy file says who may do what on which devices. port49 policy test loads it the way port49d does and runs its tests:.

It is one YAML document. An unknown key, a second document (a --- separator) or a key this build does not read is refused, and the error names the key or the line.

Paths below use [] for an item of a list and <name> for a key you choose. Every table row is a leaf; a key whose value is a block is documented by the keys under it. Durations need a unit, for example 60s.

Rules are written as words: * for one word, ** for any number, and /regex/ or @class for a term. See the policy guide for the grammar. Rules are never quoted in an error: a refusal names the list and the position.

Top-level keys

Key Type Description
platform_set string Pins the platform set (the shipped adapters) this policy renders with, for example 1.0.0, so that a binary upgrade never changes what the policy renders. Unset means set 1.0.0. A set the binary does not ship is refused, and the error lists the sets it does ship.

defaults

Key Type Description
defaults.per_login_mfa.accept list of string The proofs that satisfy the requirement: a list of hardware_key_touch, idp_mfa or both, each at most once. Required when per_login_mfa is present; leave the block out for no requirement.
defaults.per_login_mfa.reuse_for string How long one proof counts for later logins, a duration from 0m (a proof at every login) to 8h. Default 15m.
defaults.per_login_mfa.applies_to_machines boolean Set true to require the proof of machine identities too. Default false.

platforms

Key Type Description
platforms.<name>.supports list of string The capabilities the adapter declares, a list that must include session. Replaces the inherited list.
platforms.<name>.privilege map of list of string Per privilege level, the attribute strings the reply carries at that level, for example readonly: ["priv-lvl=1"]. A level may not send an attribute twice.
platforms.<name>.ceiling_levels map of integer Per level, the priv-lvl number (0 to 15) the level stands for where the rendering sends no priv-lvl, used for the token's ceiling.
platforms.<name>.entity_classes map of list of string Named sets of device entity names, as lists of RE2 patterns, that rules reference as @class. A class name is lowercase letters, digits and _, starting with a letter, and a class may not be empty.
platforms.<name>.expands_abbreviations boolean Whether the device sends resolved command words. Needed when the platform sends per-command authorization.
platforms.<name>.splits_interface_names boolean Whether the device sends an interface name and its index as two arguments.
platforms.<name>.pipe_token string The modifier separator, one argument with no whitespace, for example \|.
platforms.<name>.pipe_authorized boolean Whether the device sends the part of a command after the pipe for authorization.
platforms.<name>.strip_cr boolean Whether commands end in <cr>, which port49 strips before matching.
platforms.<name>.shows_server_msg boolean Whether the device displays a denial's server_msg to the operator.
platforms.<name>.privilege_attributes list of string The attribute names that carry privilege on this platform. The only names an attributes: placeholder may go in, and the names whose change port49 policy diff gates.
platforms.<name>.inert_attributes list of string The attribute names held to carry no privilege. A name may not also be in privilege_attributes, and a name a level renders is refused here.
platforms.<name>.accepted_attributes list of string Where set, every attribute name the session reply may carry. A reply with any other, or a priv-lvl the levels do not render, quarantines the device. An empty list is refused: list the names or remove the key.
platforms.<name>.role_order map of list of string Per attribute that names a role, its values lowest first, where the lab measured one fencing less than another. A value not listed ranks above every listed one. priv-lvl ranks by its number and may not be listed.
platforms.<name>.device_config list of string The device configuration the adapter's renderings depend on, one printable ASCII line per item, printed by port49 platform show.
platforms.<name>.enable_username string The username the device sends in an enable exchange. The only accepted value is principal, the operator's own username. $enab{level}$ is refused at load.
platforms.<name>.redact.[].match list of string Words that select the command this pattern applies to, * for one word and ** for any number, at most two **. No regex or class.
platforms.<name>.redact.[].after string Remove every word after the first word equal to this one. One word. Give after or from, not both.
platforms.<name>.redact.[].from integer Remove every word from this position on; the first argument is 1, and 0 is refused. Give from or after, not both.
platforms.<name>.verified string Port49's own claim about its lab coverage of a built-in adapter. Refused in a policy file: an adapter written here is unverified.
platforms.<name>.unreachable map of list of string Per level, the commands the device refuses in its own parser, as rules. The dead-rule report reads them.
platforms.<name>.unreachable_note map of list of string Port49's own record of a block's unreachable rules the set did not measure. Refused in a policy file; write the rules under unreachable.
platforms.<name>.fallback map of string Per level, what the device gives a session on its own when port49 cannot answer, as text. Not empty.
platforms.<name>.fallback_note map of string Port49's own record of a block's fallback text over the set's. Refused in a policy file; write the text under fallback.
platforms.<name>.local_accounts list of string RE2 patterns, each with one capture group (the account's name), that find a device-local account in the device's configuration. Read by port49 devices check-config.
platforms.<name>.config_checks.[].match string An RE2 pattern over one configuration line that the device's configuration must not carry. Required.
platforms.<name>.config_checks.[].says string What the matching line costs, shown when it is found. Required.
platforms.<name>.command_projection string How rules render for a session-only platform. The only value this build renders is junos_regex. Only a platform the pinned set projects may set it; elsewhere it is refused.
platforms.<name>.bits map of list of string On a projected platform, each permission bit's reach in rule form. A block may add to a bit's reach.
platforms.<name>.carve_outs map of list of string On a projected platform, the commands a bit alone makes available. A block may drop one and never add one.
platforms.<name>.bit_delivery string How permission bits travel to the device: user_permissions (as a user-permissions= attribute of the session reply) or local_user_name (as the name of a template user on the device, see local_user_names). Required on a projected platform.
platforms.<name>.local_user_names map of string Under bit_delivery: local_user_name, each level's template user on the device, named p49-<name>. Never remote or a platform system user.
platforms.<name>.rpc_entry_denies list of string On a projected platform, the denies a reply adds where a wire deny is the only guard beneath a sent bit. Each is words with a trailing **.
platforms.<name>.deny_reach_bits list of string On a projected platform, the bits a reply may send where the grant permits a carve-out of the bit.
platforms.<name>.replace boolean Set true to discard the shipped adapter of this name and start from the generic adapter. Not with based_on.
platforms.<name>.based_on string Start this new platform from another platform of the pinned set. Not for a name the set already ships, and not with replace.

privilege_levels

Key Type Description
privilege_levels.[].name string The level's name. Required and unique. The list must include readonly, operator and admin, which keep that relative order.
privilege_levels.[].parent string The level this one renders as where a platform has no rendering for it. Must be declared, and the reserved levels have no parent.

scopes

Key Type Description
scopes.[].name string The scope's name. Required and unique. Grants refer to it.
scopes.[].match map of string, list of string or comparison Selects devices by inventory attribute: each key is an attribute (site, role, platform, os_version and so on), each value a string, a list of strings, or a comparison such as {lt: "21.4R3-S5"} or {re: "..."}. Empty selects every device. name takes names only, not a comparison.
scopes.[].on_missing string What to do with a device that has no value for a match key: include or exclude. Required on a scope that attaches a deny or carries a grant with a deny, and where per_login_mfa is set. Default exclude.
scopes.[].security_log boolean Whether a device joining or leaving the scope is logged as a security record. A scope that delivers a deny is logged unless this is false.
scopes.[].attached_roles.humans list of string Roles every human principal gets on the devices in the scope, without a grant. On an include scope only roles with no permit, privilege or attributes are accepted.
scopes.[].attached_roles.machines list of string Roles every machine principal gets on the devices in the scope, without a grant. Same limit on an include scope.
scopes.[].per_login_mfa.accept list of string The proofs that satisfy the requirement: a list of hardware_key_touch, idp_mfa or both, each at most once. Required when per_login_mfa is present; leave the block out for no requirement.
scopes.[].per_login_mfa.reuse_for string How long one proof counts for later logins, a duration from 0m (a proof at every login) to 8h. Default 15m.
scopes.[].per_login_mfa.applies_to_machines boolean Set true to require the proof of machine identities too. Default false.

roles

Key Type Description
roles.[].name string The role's name. Required and unique. baseline, reach_deny and rpc_entry are port49's own and refused.
roles.[].privilege string The privilege level the role grants. Must be a declared level.
roles.[].reason string Free text recorded with the role, for review: why it exists.
roles.[].permit list of string Command rules the role permits, as words. ** is any number of words; /regex/ and @class are terms.
roles.[].deny list of string Command rules the role denies. A permit wins over a matching deny only when it is more specific than every deny that matches (dominance; see Policy). A class used in a deny may not turn case-insensitivity off.
roles.[].pipe.permit list of string Rules for the part of a command after the pipe that the role permits. Present with an empty list means no pipe is allowed.
roles.[].attributes map of list of string Per platform (or all), attribute strings added to the reply. The key must be all, a platform port49 ships or one the inventory names.
roles.[].max_session_age string How long a session may last, a duration of at least 1m, or never.
roles.[].per_login_mfa.accept list of string The proofs that satisfy the requirement: a list of hardware_key_touch, idp_mfa or both, each at most once. Required when per_login_mfa is present; leave the block out for no requirement.
roles.[].per_login_mfa.reuse_for string How long one proof counts for later logins, a duration from 0m (a proof at every login) to 8h. Default 15m.
roles.[].per_login_mfa.applies_to_machines boolean Set true to require the proof of machine identities too. Default false.

subjects

Key Type Description
subjects.[].name string The subject's name. Required and unique.
subjects.[].group string A directory group of people, as a DN or a group name. Give group or identity (or machine_group), exactly one. The usernames come from the directory; do not set username.
subjects.[].machine_group string A group of workloads, resolved through the workload mapping. Not with group. Do not set username.
subjects.[].identity string or list of string A list of principals: user:<email> (lowercase), a SPIFFE ID, system:serviceaccount:<namespace>:<name> or app:<client_id>. Do not mix people and machines in one subject; a principal belongs to one subject.
subjects.[].username string The username the device sends, for an identity subject that has no directory to derive it from. Printable ASCII, already canonical (lowercase, no DOMAIN\ prefix, no @suffix), unique across subjects, and never remote, p49-* or a platform system user.
subjects.[].enable string How the subject escalates: none (default), auto or token.
subjects.[].token_ttl duration How long a device token lives, 30s to 900s. Required on a subject a grant names.
subjects.[].api list of string The broker API rights the subject holds, a list from revoke_others, explain_others, lift_revocations, forget_principals, reset_second_factor, unlock_sign_in, import_enrollments, sync, keys_show and replay.

grants

Key Type Description
grants.[].subject string The subject that gets the roles. Must be declared.
grants.[].scope string The scope the grant applies in. Must be declared.
grants.[].roles list of string The roles granted, at least one, each declared and listed once. Two grants with the same subject, scope and roles are refused: merge them.
grants.[].max_privilege string A ceiling on the privilege level a token from this grant may carry. A level some platform numbers.
grants.[].expires string When the grant stops matching: a date (2026-12-01, the start of that day, UTC) or an RFC 3339 time.
grants.[].attributes map of list of string Per platform (or all), attribute strings the grant adds to the reply.

tests

Key Type Description
tests.[].subject string The subject the test runs as. Give subject or principal, one of the two.
tests.[].principal string A principal identity the test runs as, in any form identity takes.
tests.[].device string The inventory device the test runs against. Required.
tests.[].command string The command to authorize. A test without a command is a session test.
tests.[].expect string permit or deny. Required with a command; not allowed on a session test.
tests.[].reason string Optional, with a command. The reason code the decision must carry for the test to pass, on a permit or a deny: for example no_grants_matched or permit_dominates. A code that is not on the reason codes page is refused at load. Not allowed on a session test.
tests.[].level string Session test: the privilege level the subject must be minted a token at.
tests.[].max_privilege string Session test: the ceiling the token must carry, a level or none.
tests.[].priv_lvl string Session test: the priv-lvl number (0 to 15) the reply must carry, or none.
tests.[].enable string Session test: the enable mode the subject must have, none, auto or token.
tests.[].reply list of string Session test: the exact session reply, in order, as a list of attribute strings. An empty list asserts an empty reply.
tests.[].api list of string Session test: the broker API rights the subject must hold, in any order.
tests.[].per_login_mfa none or mapping Session test: the effective per-login requirement on the device, none or a mapping of accept and reuse_for.

acknowledge

Key Type Description
acknowledge.[].role string Names a deny by its role. Give role and rule together.
acknowledge.[].rule string The deny rule as written in the role. It must exist.
acknowledge.[].platform string Names an adapter edit by its platform. Give platform, field and sha256 together.
acknowledge.[].field string The adapter key the edit changed.
acknowledge.[].sha256 string The digest port49 policy diff prints for the key's new value, 64 lowercase hex digits. The entry acknowledges that edit and no later one.
acknowledge.[].reason string Why the change is accepted. Required; review reads it beside the change.

key_groups

Key Type Description
key_groups.[].match.name string Matches one device by its inventory name. A group matches on exactly one of name, cidr or attributes.
key_groups.[].match.cidr string Matches the devices whose address is in this prefix. Written without host bits, for example 10.0.0.0/8.
key_groups.[].match.attributes map of string Matches devices whose inventory attributes equal all of these pairs.
key_groups.[].key_ref string The key (a name under keys:) that devices in the group are admitted under. Required. It names the key and never holds it.

keys

Key Type Description
keys.<name>.current.version integer Version number, 1 or greater and unique within the key. The key name is lowercase letters, digits and _, starting with a letter.
keys.<name>.current.secret_ref secret reference Where the shared secret is read from: env:NAME or file:/absolute/path. Required. The secret itself never appears in this file. port49d reads each version's secret once, when the version comes into force (at start, or in a policy reload that adds it); later reads, at SIGHUP and every 30s, only report a file that has changed or can no longer be read, and port49d keeps the secret it read. A changed secret goes in a new file under a new version.
keys.<name>.current.retire_after string Not allowed on the current version or a retired one. On an accept version, the last day (UTC, YYYY-MM-DD) it still admits; it is refused from midnight after that day.
keys.<name>.accept.[].version integer Version number, 1 or greater and unique within the key. The key name is lowercase letters, digits and _, starting with a letter.
keys.<name>.accept.[].secret_ref secret reference Where the shared secret is read from: env:NAME or file:/absolute/path. Required. The secret itself never appears in this file. port49d reads each version's secret once, when the version comes into force (at start, or in a policy reload that adds it); later reads, at SIGHUP and every 30s, only report a file that has changed or can no longer be read, and port49d keeps the secret it read. A changed secret goes in a new file under a new version.
keys.<name>.accept.[].retire_after string Not allowed on the current version or a retired one. On an accept version, the last day (UTC, YYYY-MM-DD) it still admits; it is refused from midnight after that day.
keys.<name>.retired.[].version integer Version number, 1 or greater and unique within the key. The key name is lowercase letters, digits and _, starting with a letter.
keys.<name>.retired.[].secret_ref secret reference Where the shared secret is read from: env:NAME or file:/absolute/path. Required. The secret itself never appears in this file. port49d reads each version's secret once, when the version comes into force (at start, or in a policy reload that adds it); later reads, at SIGHUP and every 30s, only report a file that has changed or can no longer be read, and port49d keeps the secret it read. A changed secret goes in a new file under a new version.
keys.<name>.retired.[].retire_after string Not allowed on the current version or a retired one. On an accept version, the last day (UTC, YYYY-MM-DD) it still admits; it is refused from midnight after that day.