Skip to Content
ReferenceConstitution

Constitution

The constitution is the Rego policy TruStacks writes and every proposal is judged against. It is Apache 2.0, lives in the public trustacks-policy repository, and is published as a signed OCI artifact. Free at all tiers.

The runner evaluates it with OPA inside your cluster, after the DevOps Engineer emits a proposal and before anything is written to your platform repo. A proposal with any deny never becomes a pull request.

The rules

Twenty-four rules in three kinds. The kind decides what a rule can do. Eight of them apply only to ECS Fargate targets (Beta), and four only to Kubernetes targets; see Which targets a rule applies to.

KindCan refuse a proposalFeeds the maturity score
GateYesNo
BehaviorYesYes, as a practice row
PostureNo, neverYes, as a tooling row

Gate rules

Is the artifact the right shape, in the right place?

Rule IDWhat it checks
proposal.allowed_pathsEvery file lives under gitops/, argo-apps/, or .github/workflows/.
proposal.no_path_traversalNo path is absolute or contains a .. segment.
proposal.has_workflowThe proposal includes at least one .github/workflows/*.yaml file.
proposal.has_helm_chartKubernetes, Helm renderer: at least one Chart.yaml under gitops/.
proposal.has_kustomizationKubernetes, Kustomize renderer: at least one kustomization.yaml under gitops/.
proposal.has_argocd_applicationKubernetes: at least one argo-apps/argo-apps-<cluster>/<service>-application.yaml.
argocd.repoURL_is_canonicalEvery ArgoCD Application’s spec.source.repoURL equals your platform repo URL.
proposal.has_ecs_task_definitionECS Fargate: a gitops/<application>/<target>/<service>/task-definition.json.
proposal.has_ecs_deploy_workflowECS Fargate: a workflow that deploys the task definition with aws-actions/amazon-ecs-deploy-task-definition.

Only the renderer check that matches your Application’s renderer applies, and only on a Kubernetes target. A Fargate target has no renderer.

Behavior rules

Is the artifact doing the right thing? Each one maps to a practice key in your environment profile.

Rule IDWhat it checksPractice
practice.workflow_has_test_stepEvery CI workflow has a run: step that runs tests: pytest, npm test, yarn test, pnpm test, dotnet test, go test, or a Maven test/verify or Gradle test/check/build.tests_in_ci
practice.workflow_has_lint_stepEvery CI workflow has a lint or static-analysis step: ruff, eslint, biome, golangci-lint, go vet, staticcheck, dotnet format, checkstyle, spotless, spotbugs, pmd, or -Dlint.linting_in_ci
practice.argocd_prod_requires_manual_syncKubernetes: when the proposal’s target cluster has tier prod, its ArgoCD Application has no spec.syncPolicy.automated, whatever the cluster is named. Runners older than 0.2.14-rc25 send no tier, so the rule falls back to argo-apps/argo-apps-prod/ (a cluster named prod).manual_promotion_to_prod
practice.dockerfile_runs_as_nonrootEvery Dockerfile in the proposal has a USER that is not root or 0.non_root_containers
practice.workflow_pins_action_versionsEvery third-party uses: pins a full 40-character commit SHA. actions/* and local ./ actions are exempt.supply_chain_pinning
practice.ecs_task_runs_as_nonrootECS Fargate: every container sets user, and it is not root, 0, root:* or 0:*. (AWS Security Hub ECS.20)non_root_containers
practice.ecs_task_readonly_root_filesystemECS Fargate: every container sets readonlyRootFilesystem: true, spelled exactly that way. (ECS.5)none yet
practice.ecs_task_secrets_not_in_environmentECS Fargate: no environment entry whose name or value looks like a credential, and every secrets[].valueFrom is a full Secrets Manager or SSM ARN. (Stricter than ECS.8)none yet
practice.ecs_task_image_is_pinnedECS Fargate: every image has a digest or a non-floating tag (not latest, main, prod and the like, and not untagged). A no-floating-tags check, not a pin: see below.none yet
practice.ecs_task_configures_loggingECS Fargate: every container sets logConfiguration.logDriver. (ECS.9)none yet
practice.workflow_prod_deploy_requires_environmentECS Fargate, prod-tier target: every job that runs amazon-ecs-deploy-task-definition declares a non-empty environment:.none, deliberately

The test-step and lint-step rules judge CI workflows. A Fargate deploy workflow applies an artifact and has no suite to run, so it is skipped; the CI workflow beside it is still judged.

Why most Fargate rules score nothing yet. A practice key is a row on every customer’s score. Adding one that only Fargate can check would add a row Kubernetes customers could only self-attest, and lower their coverage for a check that does not apply to them. So these rules refuse bad Fargate proposals today and score nothing. ecs_task_runs_as_nonroot is the exception: its intent is the same as the Dockerfile rule’s and it is enforced in the artifact, so it feeds non_root_containers.

Why the prod-environment rule never scores. It proves the deploy job names a GitHub environment. Whether that environment has required reviewers is a repository setting TruStacks does not read, and on private repositories it depends on your GitHub plan. If the rule’s silence counted as “a human approves promotion to prod: verified”, the score would claim something nobody checked. For a Fargate Application, that practice can only be claimed, by your attestation.

Tags are not image identity. A commit-SHA tag can be overwritten by anyone who can push to your registry, and ECS resolves the tag when it deploys. ecs_task_image_is_pinned guarantees there is no floating tag. It does not guarantee the image that runs is the one that was reviewed. Until deploys pin a digest, that guarantee does not exist.

Which targets a rule applies to

Every rule declares the target kinds it applies to in its metadata (target_kinds), beside the tiers it applies to (tier_scope). A rule without target_kinds applies to every kind.

target_kindsRules
kubernetesproposal.has_helm_chart, proposal.has_kustomization, proposal.has_argocd_application, practice.argocd_prod_requires_manual_sync
ecs-fargateproposal.has_ecs_task_definition, proposal.has_ecs_deploy_workflow, the five practice.ecs_task_* rules, practice.workflow_prod_deploy_requires_environment
anyeverything else

argocd.repoURL_is_canonical declares no kind, but it only inspects ArgoCD Applications, so it never fires on a Fargate proposal. The runner tells the policy which kind and tier a proposal targets (input.context.target). A target with no kind is Kubernetes, so policy judges older runners exactly as before. A policy bundle older than 1.3.0 knows nothing about Fargate and refuses a Fargate proposal for having no ArgoCD Application: it fails closed.

Posture rules

Do you run the tools? These never refuse a proposal. A missing scanner is a gap in your posture, not a broken artifact, so it shows up in your gap report and your score instead of blocking delivery.

Rule IDCategory it scores
posture.image_scanning_declaredimage_scanning
posture.sast_sca_declaredsast_sca
posture.secret_scanning_declaredsecret_scanning
posture.sbom_signing_declaredsbom_signing

The naming rule

constitution.overlay_rule_naming checks the IDs of your own overlay rules against the rule-naming standard. It judges overlay rule metadata, not proposals.

The Rules page (#/rules) lists every rule your runner reports, with its description, tooling categories, and Rego source. Filter by Structural (gate) or Behaviors.

How the score uses the rules

Two rule fields drive the maturity score:

  • required_tooling_categories puts a tooling row on the scorecard. Today that is six categories: ci_cd_platform (from proposal.has_workflow), gitops_controller (from proposal.has_argocd_application), and the four posture categories.
  • practice_dimensions puts a practice row on the scorecard: the five practice keys above. Two rules feed non_root_containers (the Dockerfile rule and the ECS task rule); the other Fargate rules feed none yet.

Gate rules with neither field are enforcement only. They never move your score.

How the policy reaches your runner

The constitution is published from the trustacks-policy repository.

Artifactghcr.io/trustacks/policy/constitution
Current version1.3.0, also tagged 1 and latest
Signed byhttps://github.com/TruStacks/trustacks-policy/.github/workflows/publish.yml@refs/tags/v<version>
SigningSigstore keyless, Rekor transparency log entry per release

Hosted runners follow the :1 tag. Fixes in the 1.x line reach your runner on its next start, without a runner upgrade. A 2.0 that needs a newer runner never arrives on a tag your runner follows.

At start, the runner’s load-policy-bundle init container resolves :1 to a digest, pulls that digest, and verifies its signature. If anything fails it falls back to the copy of the constitution baked into the runner image and reports why. See Policy in force.

⚠️

Runner versions. Signed-policy pull for hosted runners ships in runner 0.2.14-rc24 and later, which are pre-releases; no stable release includes it yet. Earlier runners, including 0.2.13, enforce the copy baked into the runner image and do not verify a signature at start.

Policy versions independently of the product. The constitution is on the 1.x line; runner and control-plane releases are on 0.2.x. See Versioning.

Verify the policy yourself:

cosign verify \ --certificate-identity-regexp '^https://github\.com/TruStacks/trustacks-policy/\.github/workflows/publish\.yml@refs/tags/v.*$' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ ghcr.io/trustacks/policy/constitution:1

Your own rules

Overlay rules let you add your own reasons to refuse a proposal. Their denies are unioned into the constitution’s (data.proposal.deny), so an overlay can only add refusals. It has no way to cancel a constitution deny. That holds by how the policy is assembled, not because a tool proves it.

What works today:

  • Drafting. Type /rule new <id> in the Workspace chat and the Coordinator drafts a rule grounded in your environment profile. Drafts are not saved; copy or download them.
  • Authoring and testing. trustacks rule new, test, and lint work on a local checkout. See the Rule CLI.
⚠️

No delivery path to hosted runners yet. trustacks rule sign publishes to a local development registry with a local key, and the hosted runner chart does not load overlay bundles. Per-customer signing keys are not built. Until they are, overlay rules run only on local and self-hosted setups.

What is not in the constitution

  • Rules about approvals, review counts, or who may merge. Merge control belongs to your git provider’s branch protection.
  • Rules about credentials, data residency, or citations.
  • Waivers. There is no waiver mechanism.
  • SOC 2, HIPAA, PCI, or FedRAMP rules. See Specialist Packs.

Where to go next

Last updated on