CEN
← Integration
Ready to install · Version 1.0.0

Your community. Your router.

Run your own PHP/MariaDB routing site, manage your blocks and numbers, and connect it to CEN.

Download Community Router
Self-hostedPHP + MariaDBYour database and administrator account.
CloudPanel readyNo build stepUpload, configure, install over SSH.
FederatedCEN compatibleLocal routing stays under your control.
Verify the download

SHA-256 for the ZIP:

e879cc13b998fbebaadfc7d66b01ca6d13eff60a69478289e84b89568fe745f5
Download checksum

CEN Community Router 1.0.0

A standalone PHP application based on the supplied block-prefix IAX router example. Manage your own community directory and connect it to CEN without moving your blocks into CEN. The application performs routing lookups. Asterisk/FreePBX still carries calls and provisions extensions.

Included

  • Private administrator login, password recovery over SSH, CSRF protection and throttling.
  • MariaDB/MySQL block directory, listed-number or whole-block routing, per-block status and inbound/outbound switches.
  • Hashed, revocable PBX and member API credentials. Encrypted IAX passwords and outgoing CEN key.
  • Local community dialing and optional CEN global dialing; a compatible external-member JSON API.
  • Per-block FreePBX ZIP downloads, all twelve CEN intercept codes, administration history and a cron health check.
  • No default administrator, hosted dependencies, Composer install or JavaScript build step. No live CEN credentials are included.

CloudPanel installation

  1. Create a Generic PHP site in CloudPanel with your own hostname, site user, and PHP 8.2 or newer. Install a valid TLS certificate. PHP extensions: pdo_mysql, mbstring, sodium, curl, and zip. Use a matching CLI PHP version for installation/cron.

  2. Create a separate empty database and database user in the site's Databases tab. Grant that user access to this database only. Do not use CEN's central database or its credentials.

  3. Upload/extract this entire directory into the site's directory, for example /home/your-site-user/htdocs/router.example.org. In site settings, change Root Directory to /home/your-site-user/htdocs/router.example.org/public. Only public/ may be web-accessible. The API uses real .php entry points and does not need a custom rewrite rule.

  4. Log in over SSH as that site user (the same user as its PHP-FPM pool):

    cd /home/your-site-user/htdocs/router.example.org
    cp config.example.php config.php
    chmod 600 config.php
    # Edit config.php with your editor: HTTPS URL, name, approved area code,
    # database host/name/user/password. Leave app_key empty.
    php8.4 bin/install.php
    

    Use your configured PHP version if it is not 8.4. The installer prompts for your administrator email and password, generates an encryption key, and creates the tables. It refuses to overwrite an existing installation. There is no public web installer.

  5. Open your hostname and sign in. Add blocks with your PBX's reachable hostname and UDP IAX port. Start in maintenance, add numbers, install the generated PBX configuration, then activate the block.

  6. Add a CloudPanel site-user cron entry, substituting your actual path/version:

    */5 * * * * /usr/bin/php8.4 /home/your-site-user/htdocs/router.example.org/bin/health.php >/dev/null 2>&1
    

    This checks database access and prunes expired throttle counters. It does not claim to test IAX reachability or call audio. The dashboard shows the last successful check.

  7. Verify https://your-host/config.php, /database/schema.sql, and /storage/ return 404/403, and that only /public content is served. If private files are reachable, correct the document root before entering credentials.

CloudPanel handles the site and database; this package does not install CloudPanel or modify its global Nginx configuration. Preserve CloudPanel's TLS, PHP-FPM and .well-known settings. Its PHP backend should set HTTPS on, as the standard secure PHP site template does. Serve the application at the root of a hostname, not a URL subdirectory.

Official CloudPanel references:

Connect to CEN

Your CEN Area Code is three digits. A local number is seven digits (XXXYYYY); the global number is ten (AAAXXXYYYY). Preserve leading zeroes. The access prefix, normally 88, is not part of the number sent to an API.

  1. Register/verify your CEN account and get a community approved. Its area code must match config.php.
  2. In the CEN workspace, select community-operated/external routing, set the Routing API URL to https://your-host/api/route.php, and configure inbound/outbound permissions.
  3. On CEN's Routing page, generate Token CEN sends to your API (cen_hubreq_…). Paste it into this router's CEN connection → Incoming token. The router stores only its SHA-256 hash. Replacing it invalidates the old token here immediately.
  4. Generate a CEN network API key (cen_net_…) in API access. Paste it into this router's outgoing-key field and enable outgoing CEN lookups. This key is encrypted and sent only to https://api.cenpbx.com/v1/dial.
  5. Generate/copy the network IAX password from CEN's Routing page. Save it here, then download/apply the config for every receiving PBX. The current CEN external-network contract uses one from-cen password per network across the destination hosts your API returns. The secret is not sent in member API JSON. Local community calls use separate, automatically generated per-block from-community passwords.
  6. Run CEN's Routing API test, then make local and inter-community test calls in both directions. A successful lookup does not prove that UDP IAX, authentication, dialplan or audio works.

The application does not register a network automatically, create a CEN account, change DNS, provision PBX extensions, or upload your directory. Local calls continue when CEN is disabled/unreachable; failed global lookups stop with an intercept and never fall through to the PSTN.

APIs

1. CEN / trusted member → your directory

GET https://your-host/api/route.php?number=3151234

Headers: Authorization: Bearer <CEN incoming token or read-only member key>, Accept: application/json.

Exactly seven ASCII digits. API keys in query strings are not accepted. Success:

{"success":true,"status":"route","destination":{"protocol":"iax2","host":"pbx.example.net","port":4569,"block":"315","number":"1234"}}

Unknown/unavailable numbers return HTTP 200 with an intercept:

{"success":true,"status":"intercept","code":"CEN-08"}

Authentication/input/rate errors use HTTP 401/422/429 and success:false. Wrong method is 405; service failures are 503. Responses are no-store and contain no IAX passwords. Normal CEN member requests time out after four seconds, so keep the endpoint publicly reachable over valid HTTPS and fast. Do not put a CAPTCHA, browser challenge, redirect, Basic Auth or login screen in front of this path; retain the bearer authentication.

CEN currently maps any member intercept to CEN-08 for the caller, and an invalid/unreachable member API to CEN-05. The router's own local API retains the more specific code (such as CEN-03 or CEN-09). To advertise network-wide suspension/maintenance to CEN callers, set that status in CEN as well.

2. Your PBX/backend → your dialing endpoint

POST https://your-host/api/dial.php, content type application/x-www-form-urlencoded.

Fields: num=3151234 (or global 2143151234), ani=2771000; authenticate with Authorization: Bearer cr_… or POST form field auth. number is accepted as a POST alias for num for migration from the original example. GET auth/number/ani is deliberately not supported because query strings leak into logs.

Use a PBX key issued for the caller's block, here 277. The server checks that ANI is seven digits in that block and that the block is active with outbound enabled. ANI alone is not authentication and is not an assertion of end-user identity.

HTTP 200 is a plain-text sensitive result:

IAX2/from-community:<destination-local-secret>@pbx.example.net:4569/2143151234

Local seven-digit calls and global calls inside your own area resolve from your own database. Other ten-digit destinations are sent to CEN with your stored network key; successful CEN results use IAX2/from-cen:… and are validated against the requested number. No customer browser should ever receive these responses.

Errors are plain CEN-01 through CEN-12, also in X-CEN-Code. HTTP 401: wrong/revoked key; 403: originating block restricted; 422: malformed ANI/number; 429: rate limit; 404: destination intercept; 503: unavailable. Respect Retry-After. Default limits are 120 dial lookups/minute per PBX key, 600 member lookups/minute per member token, and 1200 API requests/minute per source IP.

PHP backend example

$handle = curl_init('https://router.example.org/api/dial.php');
curl_setopt_array($handle, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query(['num' => '2143151234', 'ani' => '2771000']),
    CURLOPT_HTTPHEADER => ['Authorization: Bearer '.getenv('COMMUNITY_PBX_KEY')],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 2,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
]);
$result = curl_exec($handle);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);
// Do not echo/log $result: successful results contain IAX secrets.
// Validate HTTP status, exact dial-string structure, destination and known
// intercept codes before handing the result to your restricted PBX dialplan.

Use the generated FreePBX bundle for the complete dialing flow. Other languages can use these same HTTPS contracts; no language-specific SDK is required. A CEN account password, CEN network key, incoming member token, local PBX key, and IAX password are different credentials and must not be interchanged.

Adapt your existing backend instead

You can keep your current database and implement only /api/route.php's JSON contract. Validate the incoming bearer token, require seven digits, resolve your own block/extension, return host/port/block/number as above, and return an intercept when unknown. Do not return a dial string, a redirect, HTML, a private URL or your IAX password to CEN's member client. Outgoing PBXs may call CEN /v1/dial directly, or use your own server-side authenticated bridge as this package does.

For directory-only discovery, CEN GET /v1/route?number=AAAXXXYYYY with a network bearer key returns a member handoff (area_code, network_name, routing_api, local_number). That is separate from CEN POST /v1/dial, which returns a sensitive dial string. Never forward a CEN network key to a member API; use that member's separately configured token. CEN's approved peer destinations and your own routing policy remain authoritative.

FreePBX and intercepts

Create a PBX key for each originating block. Open that block's FreePBX page, enter the key and access prefix, and download the private ZIP. Append the included snippets, create the dedicated Custom Trunk and the two prefixed outbound patterns described in its README, and configure Inbound Routes for full ten-digit numbers. Local and CEN incoming calls use from-trunk, not unrestricted from-internal.

Download all twelve announcement files once from https://cenpbx.com/assets/intercepts/cen-intercepts.zip into Asterisk's sounds/custom directory. Codes: 01 unassigned, 02 suspended, 03 maintenance, 04 retired, 05 remote network/API unavailable, 06 IAX destination unreachable, 07 invalid number/ANI, 08 number not found, 09 inbound disabled, 10 outbound disabled, 11 authentication failure, 12 service unavailable/rate limited. See https://cenpbx.com/support/intercepts for audio/transcripts. Missing recordings fall back to the built-in no-service announcement.

Test normal local calls, global calls, incoming calls, two-way audio, revoked keys, unknown and disabled numbers, CEN outage, and unreachable IAX peers. Verify 911, 988, local extensions and regular PSTN routes remain unchanged. CEN is not an emergency calling service.

Asterisk reference for the generated HTTPS options: https://docs.asterisk.org/Asterisk_21_Documentation/API_Documentation/Dialplan_Functions/CURLOPT/

Operations, recovery and updates

  • Back up the database and private config.php together. The encryption key is needed to recover saved credentials. Store backups outside public/. CloudPanel's database backup/export tools can provide database backups.
  • Use php bin/reset-admin.php as the site user to reset an existing administrator password; old sessions become invalid. Administrator sessions expire after one hour idle.
  • Rotate a PBX key by creating a new key, installing it on the PBX, testing, then revoking the previous key. Local IAX password rotation is immediate and requires updating that destination PBX. CEN network-key rotation is managed in CEN. Update shared CEN IAX passwords on all receiving PBXs together.
  • Administration history records actions, not secrets. The application does not persist dialed numbers, call audio or dial-string results. Your web server and PBXs may log query paths/numbers independently. Do not enable request-body or verbose CURL logging with live credentials.
  • This release is a single-community, administrator-operated directory, not a billing system, customer phone provisioner or media proxy. CEN's customer portal/tickets remain separate features of the hosted platform.
  • Version 1.0.0 uses schema version 1. Back up before updates; preserve config.php and storage/. Do not rerun the installer on an existing database. Later releases that change the schema must provide an explicit migration path.

Migrating the supplied example

The original index_copy.php stored blocks/API keys/dial templates in a PHP array. create_copy.php used a fixed administrator login and rewrote that PHP source. This application replaces source editing with prepared database queries. It does not execute/import untrusted PHP files or carry over the example's default password.

Add each real block/host/port through the UI, choose whole-block mode if your old router delegated extension validation to each PBX, issue fresh PBX credentials and apply the new config. Configure CEN pairing separately. Test before switching the old outbound route; keep the original files privately for reference. Do not serve the old management script publicly alongside this application.