Skip to content

Tags

Every proposed change is tagged on four axes before policy looks at it. Tagging is deterministic: the same diff and the same classify: section always produce the same tags. Each tag carries evidence (the file, the pattern, and for content matches the index of the added line), so a decision can always say why it was made. Evidence never includes the matched text, so a secret that triggers the secrets class is not copied into tags, logs or notes.

Areas, data classes and risk thresholds are defined in the classify: section of .wayglass/policy.yaml. Jurisdiction is not configurable there.

Where in the repo the change is. Each area is a list of path globs; ** matches any number of directories, including none. A change gets every area that any of its changed files matches.

The tags also record area_sets: the distinct sets of areas the changed files fall in, one per distinct set, with an empty set for a file in no area. Policy uses it to tell “touches docs” (area: docs) from “touches only docs” (only_area: docs).

classify:
area:
docs: ["docs/**", "**/*.md"]
auth: ["**/auth/**", "**/login*"]
infra: ["worker/wrangler.jsonc", ".github/**"]

What kind of data the change handles. A change has a class when any added line matches one of its content regular expressions (Go RE2 syntax), or any changed path matches one of its paths globs. Removed lines are counted but their text is never inspected.

classify:
data_class:
secrets: { content: ["(?i)api[_-]?key", "art_v[0-9]+_([a-z]+_)?[0-9a-f]{40}"] }
pii: { content: ["(?i)\\bssn\\b", "(?i)date_of_birth"] }

How big the change is. Levels are low, medium, high and critical. A change is low unless it crosses a configured threshold; the highest level whose threshold is exceeded wins. low takes no threshold.

classify:
risk:
high: { lines_changed_over: 400 }
medium: { files_changed_over: 10 }

Lines changed is added plus removed lines across all files.

Where the project’s data lives: the jurisdiction of the Artifacts namespace that holds the project’s repos, set by wayglass init --jurisdiction. The Worker passes its own namespace’s jurisdiction to the classifier and ignores anything the client claims, so a change in an EU project is always tagged eu.

This makes rules like the following enforceable, because the tag comes from where the repo physically is:

- { when: { jurisdiction: eu, data_class: pii }, action: quarantine, reason: "EU personal data needs DPO review." }

wayglass propose prints the tags for people and returns them under preview.tags in --json output; the Worker’s own tags are in decision.tags. wayglass watch shows them as each change is classified. A tags object looks like this:

{
"areas": ["auth"],
"area_sets": [["auth"], []],
"data_classes": [],
"risk": "low",
"jurisdiction": "us",
"lines_changed": 42,
"files_changed": 2,
"evidence": [
{"tag": "area", "value": "auth", "path": "web/login.ts", "pattern": "**/login*"},
{"tag": "jurisdiction", "value": "us", "detail": "namespace jurisdiction"}
]
}

To see how a classification would have gone for past commits, run wayglass policy test.