This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
MikoPBX extension module for automated SSL certificate management via Let's Encrypt (ACME v2). Uses acme.sh as the primary ACME client with support for both HTTP-01 and DNS-01 validation methods. Provides real-time certificate request progress via nchan Pub/Sub or polling fallback.
DNS-01 support enables certificate issuance without opening port 80, using DNS provider APIs (Cloudflare, AWS Route53, Hetzner, Yandex Cloud, and 150+ others). Also supports wildcard certificates (*.domain.com).
Legacy getssl client files are retained for one transition release cycle.
Source files live in public/assets/js/src/, compiled output goes to public/assets/js/:
/Users/nb/PhpstormProjects/mikopbx/MikoPBXUtils/node_modules/.bin/babel \
"public/assets/js/src/module-get-ssl-index.js" \
--out-dir "public/assets/js/" \
--source-maps inline \
--presets airbnbRepeat for each source file (module-get-ssl-index.js, module-get-ssl-status-worker.js).
phpstan analyseConfig in phpstan.neon: level 0, scans Lib/, Models/, bin/, App/, Setup/. Requires MikoPBX Core sources at ../../Core/src for class resolution. Ignores Phalcon 4/5 compatibility class-not-found errors. The bin/ scripts report Globals.php not found — this is expected (runtime file from MikoPBX DI container).
Root namespace Modules\ModuleGetSsl\ maps to repository root (see composer.json).
- Installation (
Setup/PbxExtensionSetup.php): Creates DB tablem_ModuleGetSsl, sets defaults (includingchallengeType='http'for migration), detects domain from internet interface - Runtime (
Lib/GetSslConf.php): Registers REST API callbacks, cron tasks, reacts to model changes and PBX lifecycle events - Certificate Request (
Lib/GetSslMain.php): Prepares acme.sh environment, launches async certificate request (HTTP-01 via--webrootor DNS-01 via--dns), streams progress to browser - Port 80 Management (
Lib/AcmeHttpPort.php): Temporarily opens port 80 for ACME HTTP-01 validation only — creates nginx server block at/etc/nginx/mikopbx/modules_servers/ModuleGetSsl_acme80.conf, adds iptables rules when firewall is active, uses a lock file at/var/run/custom_modules/ModuleGetSsl/acme_port80.lockwith 300s max open time and automatic stale cleanup. Skipped entirely for DNS-01. - Uninstall: Removes symlinks
/usr/bin/acme.sh,/usr/bin/getssl,/usr/share/getssl,/usr/www/sites/.well-known
Lib/GetSslMain.php— Core orchestrator. Manages directories, prepares acme.sh environment (prepareAcmeEnvironment()), builds acme.sh commands for HTTP-01 or DNS-01, launches certificate requests, monitors process completion (120s for HTTP-01 / 300s for DNS-01), pushes real-time updates via nchan, updates SSL keys in PbxSettings DB. Finds certificates in acme.sh paths first, falls back to legacy getssl pathsLib/GetSslConf.php— Module configuration hook (extendsConfigClass). Handles REST API routing (GET-CERT,CHECK-RESULT), cron task registration, and PBX lifecycle events (onAfterPbxStarted,onAfterModuleEnable). Conditionally wraps cert requests inAcmeHttpPort::openPort()/closePort()only for HTTP-01Lib/AcmeHttpPort.php— Port 80 lifecycle manager. Opens/closes nginx + iptables for ACME HTTP-01 validation. Includes stale lock cleanup (cron watchdog + PBX startup)Lib/DnsProviderRegistry.php— Static registry of 23 popular DNS providers with env variable definitions. Single source of truth for backend (form select options) and frontend (dynamic credential fields). Methods:getProviders(),getProviderById(),getProviderSelectOptions()Lib/MikoPBXVersion.php— Compatibility layer for Phalcon 4 vs 5 class names. Version cutoff at PBX 2024.2.30Models/ModuleGetSsl.php— Phalcon ORM model form_ModuleGetSsltable. Fields:id,domainName,autoUpdate,challengeType('http'|'dns'),dnsProvider(e.g. 'dns_cf'),dnsCredentials(base64-encoded JSON)App/Controllers/ModuleGetSslController.php— Web UI controller: renders form with DNS provider options, passesdnsProvidersJsonto view for JS, handles save includingdnsCredentialsApp/Forms/ModuleGetSslForm.php— Phalcon form definition with Select forchallengeType, Select fordnsProvider(with search), Hidden fordnsCredentials. Note:addCheckBox()helper exists for backward compat and can be removed whenmin_pbx_version≥ 2024.3.0
bin/acme/acme.sh— acme.sh ACME client (primary)bin/acme/dnsapi/— 168 DNS provider hook scripts for DNS-01 validationbin/getssl— Legacy getssl client (retained for transition)bin/utils/— Legacy getssl utilities (retained for transition)
All scripts bootstrap via require_once('Globals.php') which loads the MikoPBX DI container.
cronRenewCert.php— Cron entry point: conditionally opens port 80 (HTTP-01 only), runsacme.sh --cron, installs cert, closes port 80reloadCmd.php— Called by acme.sh--reloadcmdafter successful cert issuance; installs cert/key into PbxSettingsreloadCron.php— Reloads cron configuration when module settings changecleanupPort80.php— Cron watchdog (runs every minute): callsAcmeHttpPort::cleanupStale()updateCert.php— Manual cert request without nchan (runs synchronously)
public/assets/js/src/module-get-ssl-index.js— Form controller: validation, module status toggle, challenge type switching (HTTP-01/DNS-01), dynamic DNS provider credential fields based ondnsProvidersMeta, credential collection/restoration (base64 JSON), triggers API call to start certificate requestpublic/assets/js/src/module-get-ssl-status-worker.js— Real-time progress: EventSource (PBX ≥2024.2.30) or polling fallback, Ace editor for log display, 4-stage processing pipeline (STAGE_1–4)
- Browser sends
POST /pbxcore/api/modules/ModuleGetSsl/get-certwithX-Async-Response-Channel-Id: module-get-ssl-pub - Backend conditionally opens port 80 (HTTP-01 only), prepares acme.sh environment, launches acme.sh, monitors process (120s HTTP-01 / 300s DNS-01)
- Progress pushed to nchan channel
module-get-ssl-pubas JSON withmoduleUniqueId,stage,stageDetails,pid - Browser subscribes via EventSource (PBX ≥2024.2.30) or polls
check-resultendpoint - Backend closes port 80 in
finallyblock (HTTP-01 only)
- User selects DNS-01 in the UI, picks a DNS provider, enters API credentials
- Credentials stored as base64-encoded JSON in
dnsCredentialsmodel field - On cert request: credentials decoded to
export VAR='val'; ...prefix before acme.sh command - acme.sh creates
_acme-challengeTXT record via DNS provider API, validates, cleans up - No port 80 required — works behind firewalls and NAT
/usr/bin/acme.sh→{moduleDir}/bin/acme/acme.sh/usr/bin/getssl→{moduleDir}/bin/getssl(legacy)/usr/share/getssl→{moduleDir}/bin/utils(legacy)/usr/www/sites/.well-known→{moduleDir}/db/getssl/.well-known(HTTP-01 only)/usr/bin/nslookup→ busybox
acme.sh stores certificates in {moduleDir}/db/acme/{domain}/:
fullchain.cer— full certificate chain{domain}.key— private key
Legacy getssl paths ({moduleDir}/db/getssl/{domain}/) are checked as fallback.
Always use MikoPBXVersion for version-dependent class imports (Di, Validation, Uniqueness, Text, Logger). PBX versions ≥2024.2.30 use Phalcon 5; older versions use Phalcon 4. Do not hardcode Phalcon namespace paths.
Language files in Messages/. Translation keys prefixed with module_getssl_. English (en.php) is the reference file. Translations managed via Weblate.
- PHP 7.4+ / 8.0+, Phalcon 4 or 5
- MikoPBX Core framework (
MikoPBX\Common,MikoPBX\Core,MikoPBX\Modules,MikoPBX\AdminCabinet) - jQuery, Semantic UI, Ace Editor (from MikoPBX core frontend)
acme.shACME client (bin/acme/acme.sh, embedded bash script)- Minimum PBX version: 2024.1.114 (from
module.json)