Policyseed docs

Everything needed to run the open-source CLI, keep the policies in a Git repository, and understand the template syntax. The hosted generator and the Audit Kit use the same templates and renderer.

Quick start

Requires Node.js 18 or later. The package is published as policyseed and can be run with npx without installing.

# 1. Write a commented intake.yaml in the current directory
npx policyseed init

# 2. Edit intake.yaml (company, stack, owners, scope, dates)

# 3. Render all 22 policies into ./policies as NN-<slug>.md
npx policyseed build

# 4. Exit 1 if any policy's last revision date is older than its review cadence
npx policyseed check

build writes 22 files named 01-information-security-policy.md through 22-privacy-and-data-protection-policy.md. Each begins with an H1 of your company name and the policy title, followed by the nine standard sections. Commit intake.yaml and the policies/ directory together so the rendered documents can always be traced to the answers that produced them.

CLI reference

policyseed init  [--out <path>] [--force]
    Write a commented intake.yaml (default ./intake.yaml). Refuses to overwrite
    an existing file unless --force is given.

policyseed build [--intake <path>] [--out <dir>]
    Validate intake.yaml and render all 22 templates into <dir> (default
    ./policies) as NN-<slug>.md. Prints a table of ID, file and title.

policyseed check [--intake <path>] [--dir <dir>]
    Read each rendered policy's Revision History table, take the latest date,
    and compare it with review_cadence from intake.yaml
    (Annual = 365 days, Semi-annual = 182, Quarterly = 91).
    Exit 1 and list the overdue policies when any is past due.

policyseed list
    Print the 22 bundled templates with ID, file stem, title and owner role.

policyseed --version | --help

intake.yaml

policyseed init writes every field with a comment listing the allowed values. Enum values are case-sensitive. Lists can be written inline ([AWS, Vercel]) or as - item lines. A complete example:

company: Northwind Cloud Inc
product: Northwind
headcount: 11-50               # 1-10 | 11-50 | 51-200 | 201+
work_model: Remote             # Remote | Hybrid | Office
cloud: [AWS, Vercel]           # AWS, GCP, Azure, Vercel, Fly.io, Hetzner, Cloudflare, Other
scm: GitHub                    # GitHub | GitLab | Bitbucket | Other
cicd: GitHub Actions
idp: Okta                      # Google Workspace | Okta | Microsoft Entra | JumpCloud | None
mfa: true
mdm: Kandji                    # empty if none
password_manager: 1Password    # empty if none
data_types: [PII]              # any of PII, PHI, Payment
app_type: Web SaaS             # Web SaaS | API | Mobile | Multiple
vendors:
  - Supabase
  - Stripe
  - Datadog
security_owner: "Dana Whitfield, CTO"
approver: "Priya Natarajan, CEO"
incident_contact: security@northwindcloud.example
backup_tool: AWS Backup
backup_cadence: Daily
logging_tool: Datadog
tsc_scope: [Security, Availability]   # Security, Availability, Confidentiality
review_cadence: Annual                # Annual | Semi-annual | Quarterly
effective_date: 2026-09-02

Required fields: company, product, security_owner, approver, incident_contact, and at least one entry in cloud and tsc_scope. Free-text fields (cicd, mdm, password_manager, backup_tool, backup_cadence, logging_tool) may be left empty; the templates switch to generic wording when they are.

GitHub Action: fail when a policy review is overdue

policyseed check exits with status 1 when any rendered policy’s most recent revision date is older than the review_cadence allows. Running it on a schedule turns the annual policy review into a failing job rather than a forgotten calendar entry. The workflow below also runs on pull requests that touch the intake or the policies.

# .github/workflows/policy-review.yml
name: Policy review check

on:
  schedule:
    - cron: "0 9 * * 1"   # every Monday 09:00 UTC
  pull_request:
    paths:
      - "intake.yaml"
      - "policies/**"
  workflow_dispatch:

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      # Rebuild so the check runs against the committed intake, then fail the
      # job when any policy's last revision date is past its review cadence.
      - run: npx policyseed@latest build --intake intake.yaml --out policies
      - run: npx policyseed@latest check --intake intake.yaml --dir policies

When the job fails, the log lists each overdue policy with its last review date and the number of days since. To clear it, review the policy, add a row to its Revision History table with today’s date, and commit. If you regenerate from the intake, update effective_date so the new row carries the review date; check falls back to effective_date when a policy has no dated row.

Template format

Templates live in templates/ in the repository (content/policies/ in the site) as one Markdown file per policy. Each file has a front matter block and a body with exactly nine H2 sections in a fixed order. The renderer prepends the H1.

Front matter

  • id: P01 to P22. slug: the URL slug. title: the policy title.
  • short: one sentence used in indexes. owner_role: the default owner role.
  • order: 1 to 22, controls file numbering. tsc: list of criteria the policy primarily satisfies.

Sections

In order: 1. Purpose, 2. Scope, 3. Roles and Responsibilities, 4. Policy Statements, 5. Procedures, 6. Exceptions, 7. Enforcement, 8. Review Cadence, 9. Revision History. Section 4 items are written as - **4.1** text, section 5 as - **5.1** text, and section 9 is a four-column table (Version, Date, Description, Approved by) with one initial row.

Syntax

The renderer supports exactly four constructs and nothing else:

  • {{key}}: insert a value. Unknown keys render as an empty string. Booleans render as true/false; lists are joined with commas.
  • {{#if key}} ... {{/if}}: render the block when the value is truthy (non-empty string, true, non-empty list).
  • {{#unless key}} ... {{/unless}}: render the block when the value is falsy.
  • {{#each list}} ... {{/each}}: repeat the block for every item; {{this}} is the current item. Iterations are joined with newlines.

Blocks nest and may span lines. A block tag standing alone on a line is removed together with its line, so conditional paragraphs leave no blank lines behind. Runs of three or more blank lines are collapsed.

---
id: P03
slug: access-control-policy
title: Access Control Policy
short: Defines how access is requested, approved, provisioned, reviewed and removed.
owner_role: Security Owner
order: 3
tsc:
- CC6.1
- CC6.2
---

## 1. Purpose

Access control is how {{company}} makes sure that only the right people can
reach {{product}} infrastructure, source code and customer data.

## 4. Policy Statements

- **4.1** Access is granted on the principle of least privilege.
- **4.2** {{#unless idp_none}}{{idp}} is the single source of workforce identity.{{/unless}}{{#if idp_none}}{{company}} maintains a master list of every application account until a central identity provider is adopted.{{/if}}
{{#if has_mdm}}
- **4.3** Every laptop is enrolled in {{mdm}} before it receives credentials.
{{/if}}

## 2. Scope

In-scope hosting providers:
{{#each cloud_list}}
- {{this}}
{{/each}}

## 9. Revision History

| Version | Date | Description | Approved by |
|---|---|---|---|
| {{policy_version}} | {{effective_date}} | Initial release | {{approver}} |

Context keys

The intake is transformed into a flat context before rendering. These are the only keys a template can use.

KeyTypeMeaning
companystringCompany name as entered.
productstringProduct or service name.
headcountstringHeadcount bracket.
work_modelstringRemote, Hybrid or Office.
remote_or_hybridbooleanTrue unless work_model is Office.
cloudstringCloud providers joined with commas and “and”.
cloud_liststring[]Cloud providers as a list, for {{#each}}. "Other" renders as "the hosting provider" (or "other hosting providers" next to named clouds); scm "Other" renders as "the source control system".
scmstringSource control host.
cicdstringCI/CD system; defaults to “the CI/CD pipeline” when blank.
idpstringIdentity provider name.
idp_nonebooleanTrue when idp is None.
mfabooleanMFA enforced.
mdm / has_mdmstring / booleanMDM tool name and whether one is set.
password_manager / has_password_managerstring / booleanPassword manager name and whether one is set.
data_typesstringSensitive data types joined for prose.
has_pii / has_phi / has_paymentbooleanIndividual data-type flags.
has_sensitive_databooleanTrue when any data type is selected.
app_typestringWeb SaaS, API, Mobile or Multiple.
is_mobile / is_apibooleanDerived from app_type (Multiple sets both).
vendors / vendor_list / has_vendorsstring / string[] / booleanNamed vendors as prose, as a list, and whether any exist.
security_owner / approver / incident_contactstringPeople and contact as entered.
backup_tool / has_backup_tool / backup_cadencestring / boolean / stringBackup tool, whether set, and cadence (defaults to “daily”).
logging_tool / has_logging_toolstring / booleanCentral logging tool and whether set.
tsc_scopestringCriteria in scope joined for prose.
scope_availability / scope_confidentialitybooleanWhether Availability or Confidentiality is in scope.
review_cadencestringAnnual, Semi-annual or Quarterly (label form, for tables).
review_cadence_lc / review_cadence_adverbstringLowercase adjective (annual, semi-annual, quarterly) and adverb (annually, every six months, quarterly) for prose.
effective_datestringYYYY-MM-DD.
policy_versionstringAlways “1.0” for a fresh render.

The 22 templates

IDFile stemTitleOwner role
P0101-information-security-policyInformation Security PolicySecurity Owner
P0202-acceptable-use-policyAcceptable Use PolicySecurity Owner
P0303-access-control-policyAccess Control PolicySecurity Owner
P0404-authentication-and-password-policyAuthentication and Password PolicySecurity Owner
P0505-asset-management-policyAsset Management PolicyIT/Operations Lead
P0606-data-classification-and-handling-policyData Classification and Handling PolicySecurity Owner
P0707-data-retention-and-disposal-policyData Retention and Disposal PolicySecurity Owner
P0808-encryption-and-key-management-policyEncryption and Key Management PolicyEngineering Lead
P0909-change-management-policyChange Management PolicyEngineering Lead
P1010-secure-software-development-policySecure Software Development PolicyEngineering Lead
P1111-vulnerability-and-patch-management-policyVulnerability and Patch Management PolicyEngineering Lead
P1212-logging-and-monitoring-policyLogging and Monitoring PolicyEngineering Lead
P1313-incident-response-policyIncident Response PolicySecurity Owner
P1414-business-continuity-and-disaster-recovery-policyBusiness Continuity and Disaster Recovery PolicyEngineering Lead
P1515-backup-and-recovery-policyBackup and Recovery PolicyEngineering Lead
P1616-vendor-and-third-party-risk-policyVendor and Third-Party Risk Management PolicySecurity Owner
P1717-risk-assessment-and-management-policyRisk Assessment and Management PolicySecurity Owner
P1818-human-resources-security-policyHuman Resources Security PolicyPeople/HR Lead
P1919-endpoint-and-workstation-security-policyEndpoint and Workstation Security PolicyIT/Operations Lead
P2020-network-and-infrastructure-security-policyNetwork and Infrastructure Security PolicyEngineering Lead
P2121-physical-and-remote-work-security-policyPhysical and Remote Work Security PolicyIT/Operations Lead
P2222-privacy-and-data-protection-policyPrivacy and Data Protection PolicySecurity Owner

Hosted generator and Audit Kit

The web generator runs the same renderer in the browser and stores nothing server-side. The Audit Kit ($39 one-time, see pricing) sends the intake to Claude to rewrite sections 4 and 5 of each policy around your named tools, then builds DOCX files for the 22 policies, an XLSX TSC crosswalk and evidence checklist, DOCX acknowledgment forms and an ICS review calendar, all zipped in the browser. Each kit includes four tailoring passes per policy. The full policy list is at the template index.

Frequently asked questions

Does the CLI need an API key or network access?
No. The CLI renders templates locally from intake.yaml and makes no network calls. AI tailoring is only part of the hosted Audit Kit.
Can I add my own template or change the wording?
Yes. Fork the repository, edit or add Markdown files under templates/, keep the front matter and the nine section headings, and build with the CLI. The renderer ignores unknown variables, so a typo renders as an empty string; check the output.
Are the CLI output and the web generator identical?
Yes, for the same intake. Both use the same templates and the same renderer, so the Markdown is byte-for-byte the same apart from the effective date you enter.
How does the check command know when a policy was last reviewed?
It reads the Revision History table (section 9) of each rendered policy and takes the most recent date in it. When you complete a review, add a row with the date; the next check run will use it.
Is the CLI covered by the same terms as the site?
The CLI and templates are Apache-2.0 open source. They are governance policy templates, not legal advice, and using them does not guarantee any examination outcome.

Policyseed provides governance policy templates and AI tailoring. It is not legal advice and not a compliance guarantee. Management adopts the policies; the CPA firm performs the SOC 2 examination.