npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hector21/octoform

v0.5.0

Published

Declarative plan/apply governance for GitHub repositories: settings, branch rules, environments and files, from one YAML file

Readme

Octoform

npm version CI Documentation License: MIT Node.js

Declarative governance for GitHub organizations and repositories. Describe the state you intend, review a deterministic plan, and apply only the changes you approve.

Octoform works with organization-owned and personal repositories, and with the organization itself — its profile, its member policies, its teams, its custom properties and the rulesets it aims at the repositories it owns. It derives availability from the authenticated owner, repository, token, and GitHub API evidence instead of hard-coding commercial plan names.

Read the documentation · Start safely · Browse validated examples

Why Octoform

Repository templates affect future repositories, while policy drift accumulates in repositories that already exist. Octoform reconciles those existing repositories from an explicit YAML policy.

  • Review before mutation. audit and plan are read-only. apply presents the plan and asks for confirmation before writing.
  • Omission means unmanaged. A setting that is absent from the policy is left untouched; false is an explicit desired value.
  • Uncertainty blocks changes. An unreadable or unsupported setting is reported instead of guessed.
  • Owner-neutral configuration. Project names, classifications, and policy decisions live in YAML, not in ecosystem-specific source code.
  • Capability from evidence. Private repository rulesets and other gated features are decided from current GitHub behavior and token access.

Supported scope

One configuration governs one or several GitHub accounts, personal or organization, and supports:

| Area | Desired state | | --- | --- | | Repository | Features, merge methods and message defaults, metadata, topics, forking, visibility, template, archive state, and rename | | Security | Vulnerability alerts, automated fixes, private reporting, secret scanning, code-scanning setup, and immutable releases | | Branches | Default-branch rename, ensured branches, repository rulesets, and classic branch protection | | Access | Direct collaborators and team grants, with revocation stated rather than inferred | | Collections | Labels, milestones, and repository custom-property values | | Environments | Creation and required user reviewers | | Files | Safe create-if-missing seeding without overwrite or deletion | | Organization | Profile, member policies, custom-property definitions, and organization rulesets targeted by repository name or property | | Teams | Creation, nesting, visibility, and who is on each one | | Roles | Who holds each organization role, by users and by teams | | People | Read-only inventory, and explicit one-person invite, removal and conversion | | Classification | Local rules for any owner and organization custom-property synchronization |

Organization membership is the one area that is deliberately not declarative. An invitation is an act addressed to a person who is emailed about it, so it is a command that asks first rather than a line in a file a schedule reconciles.

Unsupported, unavailable, and intentionally excluded operations remain visible in the plan. See the capability boundary for the exact versioned contract and known limitations.

Install

Install the scoped package globally; the executable remains octoform:

npm install --global @hector21/octoform

Node.js 20 or newer is required. Node.js 20, 22, and 24 are tested.

Safe quick start

Create octoform.yml with one deliberately narrow setting:

owner: your-account

defaults:
  merge:
    delete_branch_on_merge: true

Provide a token through the process environment, then produce a read-only plan:

export GITHUB_TOKEN=...
octoform plan --config octoform.yml

For a fine-grained personal access token, select only the repositories and permission families needed by the declared policy. GitHub App installation tokens are preferred for automation. Never put a token in the YAML file.

Review every proposed and blocked operation. Apply only when the plan matches your intent:

octoform apply --config octoform.yml

The apply command plans again, displays the result, and requests confirmation. Use --yes only inside an independently protected automation boundary.

Continue with the complete installation, authentication, first-plan, and safe-apply guides.

Configuration model

Policy is resolved key by key from the most general layer to the most specific:

imports -> policies -> root defaults -> owner defaults -> types.<type> -> repos.<name>

A file names one account in owner, or several under owners, keyed by GitHub login. The root of a multi-owner file holds what the accounts share, and each entry states only what differs. Declaring both owner and owners is rejected, because nothing would say which account the shared layer was written for.

policies are named, reusable fragments that any layer folds in through its own policies list, before that layer's own keys. Imports compose whole files the same way. An imported file may omit the owner; the configuration being executed must name one. Unknown keys, unresolved references, conflicting owners, and import or policy cycles are all rejected, with the file and path that caused them.

Declarations that only apply to organizations stay valid under a personal account so a shared preset stays shareable; they are reported as not applicable. Pass --strict to fail on them instead.

For complete field shapes, precedence, selectors, capability behavior, and examples, use the versioned configuration reference.

Commands

| Command | Contract | | --- | --- | | octoform audit | Read-only inventory and configurable findings | | octoform plan | Read-only desired-state comparison with blocked-operation reasons | | octoform apply | Confirmed mutation based on a freshly produced plan | | octoform classify | Read-only type proposals, or explicit organization-property writes with --apply | | octoform properties sync | Organization custom-property schema and declared value synchronization | | octoform config validate | Offline load and resolution report. Never contacts GitHub | | octoform config migrate | Offline conversion from a single-owner file to the multi-owner shape | | octoform inspect config | Offline. The fully resolved configuration for the selected owners | | octoform inspect capabilities | Each selected owner's kind, identity, and what it supports | | octoform inspect members | Read-only. Who is in the organization, and which people the configuration names are not | | octoform members invite | Confirmed invitation of one person, who is emailed about it | | octoform members remove | Confirmed removal of one person, or withdrawal of the invitation they never answered | | octoform members convert | Confirmed conversion of one member to an outside collaborator |

All commands accept --config <path>. A run against several owners can be narrowed with repeatable --owner <login>, and --repo <name> accepts a qualified owner/name to resolve a name declared under more than one selected owner. Planning and apply can also be narrowed with --type <type>, bounded with --concurrency <n>, and told to stop at the first failing owner with --fail-fast instead of continuing through the rest. The members commands take the one person they are about as --user <login>, and members invite takes the organization role to offer as --role <role>.

The three members commands refuse two things whatever else is true: removing or converting the only remaining owner, which would leave an organization nobody can administer, and doing either to the account the run is authenticated as, which could not put itself back.

octoform plan --out plan.json saves the reviewed plan, and octoform apply --plan plan.json performs exactly that plan or refuses, naming which check failed. --format json wraps output in a versioned envelope. Exit status is a frozen set of classes: 0 success, 1 drift found or apply declined, 2 usage or configuration error, 3 authentication or permission failure, 4 an operation was blocked, 5 an operation or the run itself failed. The command reference documents every flag and output.

Security and automation

Octoform reads credentials from a token passed by a programmatic caller, then GITHUB_TOKEN, then GH_TOKEN. Nothing else is searched, and secrets are never accepted as command-line flags. A configuration value shaped like an issued GitHub token is rejected when the file loads, naming the YAML path without echoing the value. Plans may still reveal private repository names and settings, so their logs and artifacts require the same confidentiality as the repositories they describe; a saved plan is written with owner-only permissions where the platform supports them.

Pull-request workflows should remain read-only. Place write credentials behind a protected environment, explicit authorization, pinned dependencies, and a concurrency boundary that prevents overlapping applies.

Governing the organization needs more than governing its repositories, and the difference is worth stating before granting it. A base permission, an organization ruleset and an organization role each reach every repository the organization owns, including ones no configuration names, so each is reported as sensitive in the plan. Deleting a team takes its child teams with it, and taking somebody off a team takes them out of every repository that team reached; both are reported as destructive. Nothing at this level is removed because a file stopped mentioning it — a team, a property definition or a member is removed only where the configuration says so, or through a command that names the person and asks.

reference/permissions.json ships with the package and states, per route and per capability, which classic scope or fine-grained profile it needs. It is generated from the same register the behaviour is checked against, so it is what to read before deciding what a token should be allowed to do.

There is no rollback command, and adding one would be a lie: GitHub does not keep the previous value of most of these settings, so octoform could only restore what it happened to read a moment earlier. The way back is the way forward — correct the file and run again — which works because omission means unmanaged and a plan is deterministic. What that does not recover is anything GitHub destroyed rather than changed: a deleted team and its children, a deleted custom property definition and every value repositories held for it, a removed member's team memberships, a deleted label and its place on every issue it marked. Each of those is reported as destructive in the plan, and none of them happens because a line was left out of a file.

Four repository settings exist only in GitHub's GraphQL API and are read and written there: features.sponsorships, features.pull_requests, repo.issue_creation and repo.pull_request_creation. They are asked for only when a policy manages one of them, they need a token GraphQL accepts, and a change to one is blocked rather than attempted when the repository's node identity could not be read. Everything else is REST, and a GraphQL failure narrows to the same unreadable state a REST failure does, so the planner blocks it for the same reason without knowing which transport observed it.

Programmatic API

The package root exports the same configuration, observation, planning, apply, classification, and reporting building blocks used by the CLI. Octoform is ESM only and publishes TypeScript declarations.

See the programmatic API overview for the supported export surface and stability boundary.

Versions and support

Octoform uses complete Semantic Versioning numbers without prerelease or build suffixes. During 0.x, a breaking contract change increments the minor version; compatible fixes increment the patch version. Version branches use vMAJOR.x and the highest supported line is the default branch.

The 0.5 documentation URL follows the newest published 0.5.x package while immutable patch URLs remain available. Review the version policy and changelog before updating.

Contributing

Issues and pull requests are welcome. Start with CONTRIBUTING.md for the development workflow, testing contract, and the rule that product-specific behavior belongs in configuration.

Thanks to all contributors.

License

MIT