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 checkbuild 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 | --helpintake.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-02Required 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 policiesWhen 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 astrue/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.
| Key | Type | Meaning |
|---|---|---|
company | string | Company name as entered. |
product | string | Product or service name. |
headcount | string | Headcount bracket. |
work_model | string | Remote, Hybrid or Office. |
remote_or_hybrid | boolean | True unless work_model is Office. |
cloud | string | Cloud providers joined with commas and “and”. |
cloud_list | string[] | 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". |
scm | string | Source control host. |
cicd | string | CI/CD system; defaults to “the CI/CD pipeline” when blank. |
idp | string | Identity provider name. |
idp_none | boolean | True when idp is None. |
mfa | boolean | MFA enforced. |
mdm / has_mdm | string / boolean | MDM tool name and whether one is set. |
password_manager / has_password_manager | string / boolean | Password manager name and whether one is set. |
data_types | string | Sensitive data types joined for prose. |
has_pii / has_phi / has_payment | boolean | Individual data-type flags. |
has_sensitive_data | boolean | True when any data type is selected. |
app_type | string | Web SaaS, API, Mobile or Multiple. |
is_mobile / is_api | boolean | Derived from app_type (Multiple sets both). |
vendors / vendor_list / has_vendors | string / string[] / boolean | Named vendors as prose, as a list, and whether any exist. |
security_owner / approver / incident_contact | string | People and contact as entered. |
backup_tool / has_backup_tool / backup_cadence | string / boolean / string | Backup tool, whether set, and cadence (defaults to “daily”). |
logging_tool / has_logging_tool | string / boolean | Central logging tool and whether set. |
tsc_scope | string | Criteria in scope joined for prose. |
scope_availability / scope_confidentiality | boolean | Whether Availability or Confidentiality is in scope. |
review_cadence | string | Annual, Semi-annual or Quarterly (label form, for tables). |
review_cadence_lc / review_cadence_adverb | string | Lowercase adjective (annual, semi-annual, quarterly) and adverb (annually, every six months, quarterly) for prose. |
effective_date | string | YYYY-MM-DD. |
policy_version | string | Always “1.0” for a fresh render. |
The 22 templates
| ID | File stem | Title | Owner role |
|---|---|---|---|
| P01 | 01-information-security-policy | Information Security Policy | Security Owner |
| P02 | 02-acceptable-use-policy | Acceptable Use Policy | Security Owner |
| P03 | 03-access-control-policy | Access Control Policy | Security Owner |
| P04 | 04-authentication-and-password-policy | Authentication and Password Policy | Security Owner |
| P05 | 05-asset-management-policy | Asset Management Policy | IT/Operations Lead |
| P06 | 06-data-classification-and-handling-policy | Data Classification and Handling Policy | Security Owner |
| P07 | 07-data-retention-and-disposal-policy | Data Retention and Disposal Policy | Security Owner |
| P08 | 08-encryption-and-key-management-policy | Encryption and Key Management Policy | Engineering Lead |
| P09 | 09-change-management-policy | Change Management Policy | Engineering Lead |
| P10 | 10-secure-software-development-policy | Secure Software Development Policy | Engineering Lead |
| P11 | 11-vulnerability-and-patch-management-policy | Vulnerability and Patch Management Policy | Engineering Lead |
| P12 | 12-logging-and-monitoring-policy | Logging and Monitoring Policy | Engineering Lead |
| P13 | 13-incident-response-policy | Incident Response Policy | Security Owner |
| P14 | 14-business-continuity-and-disaster-recovery-policy | Business Continuity and Disaster Recovery Policy | Engineering Lead |
| P15 | 15-backup-and-recovery-policy | Backup and Recovery Policy | Engineering Lead |
| P16 | 16-vendor-and-third-party-risk-policy | Vendor and Third-Party Risk Management Policy | Security Owner |
| P17 | 17-risk-assessment-and-management-policy | Risk Assessment and Management Policy | Security Owner |
| P18 | 18-human-resources-security-policy | Human Resources Security Policy | People/HR Lead |
| P19 | 19-endpoint-and-workstation-security-policy | Endpoint and Workstation Security Policy | IT/Operations Lead |
| P20 | 20-network-and-infrastructure-security-policy | Network and Infrastructure Security Policy | Engineering Lead |
| P21 | 21-physical-and-remote-work-security-policy | Physical and Remote Work Security Policy | IT/Operations Lead |
| P22 | 22-privacy-and-data-protection-policy | Privacy and Data Protection Policy | Security 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.