Certification checks
Every submitted release runs through 13 automated checks before it can publish. This page explains what each one looks for and how to write your plugin so it passes.
How the tiers work
- Required -- fail any one of these and your release stays in "changes requested" and never publishes, no matter what else passes.
- Certification-gating -- these don't block publishing, but your release only reaches the CERTIFIED badge if all of them pass too. Fail one and it still publishes, just at STRUCTURAL.
- Informational -- recorded on your release's technical report, but don't block publishing or affect your certification tier.
You can see the result of every check for your own release on its version page, under "Technical report".
Required checks
Fail one of these and your release never publishes.
perl_syntax
Every .pm file in your release must compile (perl -cw)
against the Koha version you declared as minimum_version. Run
perl -cw YourPlugin.pm yourself against that Koha version before
tagging a release.
manifest_completeness
Your plugin's $metadata hash must declare both version
and license:
our $metadata = {
name => 'My Plugin',
description => 'What it does',
author => 'Your Name',
minimum_version => '23.05',
version => '1.0.0',
license => 'GPL-3.0',
};
dependency_allowlist
Your code is scanned for patterns the store won't allow in a submitted plugin, since these can't be reviewed automatically and are common vectors for something going badly wrong on a library's live system:
system(),exec(), backticks, orqxuse-ing a networking module directly (IO::Socket,Net::*,LWP,HTTP::Tiny) -- use Koha's own HTTP client wrappers insteadopen()-ing an absolute filesystem path- a string referencing
../to escape your plugin's own directory
This is a static analysis of real parsed Perl syntax, not a text search -- mentioning one of these in a comment or string won't false-positive.
Certification-gating checks
These don't block publishing, but all of them need to pass for your release to reach the CERTIFIED tier.
perl_critic
Every .pm file is run through Koha's own Perl::Critic policy set.
Run it yourself locally against your plugin's files before submitting.
docs_presence
Include a README, README.md, Development.md,
CONTRIBUTING.md, or a docs/ directory somewhere in
your repository.
readme_presence
Specifically: GitHub needs to recognize a README for your repository (the
same one it'd show on your repo's own front page -- README.md,
README.rst, or a bare README all count). It's shown
in full on your plugin's page here, so it's worth keeping it current.
changelog_format
Include a CHANGELOG.md (or CHANGES.md) with at
least one heading shaped like a
Keep a Changelog
or Common Changelog
version entry:
## [1.2.0] - 2026-01-01
### Added
- A new widget for doing the thing.
### Fixed
- The thing that was broken.
A bare ## v1.2.0 heading passes too -- this check isn't full
spec validation (it doesn't check section names, dates, or an "Unreleased"
section), just "is there a real, dated version entry here." That same
heading is what powers the "What's new in this version" summary shown on
your plugin's page, so passing this check also means that summary will
actually show up.
tests_presence
Include at least one t/*.t file in your tagged source repository.
(Your .kpz release archive itself doesn't need to contain
tests -- this check looks at the tagged commit on GitHub, not the archive.)
translatable_templates
Any .tt template rendering visible text (<h1>,
<p>, <button>, etc.) should mark that
text for translation:
<h1>[% t('Hello, world') %]</h1>
plugin_template_wrapper
Every .tt template should include Koha's plugin template
wrapper, currently doc-head-close.inc:
[% INCLUDE 'doc-head-close.inc' %]
hardcoded_credentials
Don't assign a literal secret to a credential-shaped variable name (e.g.
api_key, password) anywhere in your code, and don't
commit a PEM private key or an AWS access key. Use Koha's own configuration
mechanisms for anything a library needs to supply themselves.
Informational checks
Recorded on your release's technical report, but don't block publishing or affect your certification tier.
koha_max_version
Whether your $metadata declares a maximum_version
in addition to minimum_version. Worth setting if you know your
plugin doesn't work against newer Koha releases yet.
gpg_signed_tag
Whether the release tag itself was GPG-signed on GitHub
(git tag -s v1.2.0). This is about your own tag's authenticity,
separate from the store's own signing of the published .kpz --
see the verification key page for that.