Alynt ClassMarker Bridge
ClassMarker result bridge for Alynt Certificate Generator.
Install
The author publishes release zips, so WP-CLI can install straight from GitHub:
wp plugin install https://github.com/nichlasb/alynt-classmarker-bridge/releases/download/v0.1.0/alynt-classmarker-bridge-v0.1.0.zipDeclares an update source (NichlasB/alynt-classmarker-bridge), so updates arrive through the plugin's own updater.
Readme
Alynt ClassMarker Bridge
Companion plugin for Alynt Certificate Generator (ACG). It accepts authenticated ClassMarker result webhooks, stores a minimized event snapshot, generates one certificate per canonical result, sends the configured ACG student/admin emails, and synchronizes the approved Infinity record.
Runtime, deployment, database, and external-service state must be verified against a named target before it is treated as current. This repository documentation does not authorize credential entry, route enablement, deployment, provider changes, or production operations.
Features
- Exact-body ClassMarker HMAC verification with JSON and payload-size constraints.
- Immutable route matching and per-event route snapshots.
- Idempotent certificate, student-email, administrator-email, and Infinity stages.
- Persistent redacted event status, bounded retry controls, and retention cleanup.
- Optional aggregate-only administrator health reporting and safe JSON export, off by default.
- Server-only secret storage and safe route import/export.
- Non-production WP-CLI fixtures for separately approved runtime validation.
Requirements
- WordPress 6.0 or later and PHP 7.4 or later.
- Alynt Certificate Generator 0.3.0 or later.
- Action Scheduler (provided by ACG's dependency stack).
- ClassMarker per-assignment webhook support and a dedicated webhook secret.
- An Infinity API token with the minimum required board access.
Installation
- Build or obtain an approved release ZIP whose root folder is
alynt-classmarker-bridge. - Upload and activate the plugin through WordPress Plugins administration on the confirmed target.
- Confirm Alynt Certificate Generator 0.3.0 or later and Action Scheduler are available.
- Define provider secrets outside version control.
- Configure routes disabled, validate the immutable identifiers and templates, then follow the approval gates in
docs/OPERATIONS.md.
Activation alone does not authorize ClassMarker, Infinity, SMTP, Pabbly, or production changes.
Secret storage
Secrets are intentionally accepted only from server-side constants, normally in wp-config.php. They are never saved in the WordPress options table, shown in exports, or included in diagnostics.
define( 'ALYNT_CLASSMARKER_BRIDGE_CLASSMARKER_SECRET', 'set-outside-version-control' );
define( 'ALYNT_CLASSMARKER_BRIDGE_INFINITY_TOKEN', 'set-outside-version-control' );
The bridge settings page reports only whether each constant is configured.
ClassMarker webhook setup
After the plugin has been activated on the confirmed runtime target, use the endpoint shown on Settings → ClassMarker Bridge:
https://example.com/wp-json/alynt-classmarker-bridge/v1/results
Each approved ClassMarker assignment must use the same endpoint and its matching secret. The receiver accepts JSON only, verifies X-Classmarker-Hmac-Sha256 against the raw body, rejects oversized/malformed/unsupported payloads, and acknowledges a payload_status=verify request without creating an event or certificate.
Configure only the recovered routes approved for the pilot. A verified ClassMarker save and a controlled result delivery are later approval gates; this documentation does not authorize either external change.
Route configuration
Routes are imported/exported as a JSON array from Settings → ClassMarker Bridge. Exports omit credentials and administrator email addresses. A route has immutable ClassMarker IDs, a revision, compatible ACG template IDs, Infinity folder ID, and a declarative variable map. Human-readable ClassMarker titles are never used to route results.
See the settings and route reference for the complete validation, selector-precedence, and safe-import/export rules.
[
{
"route_id": "stable-route-uuid",
"revision": 1,
"enabled": false,
"test_id": 123,
"assignment_type": "link",
"assignment_id": 456,
"threshold": 80,
"template_id": 789,
"course_title": "Approved course title",
"certificate_title": "Approved certificate title",
"student_email_template_id": 790,
"admin_email_template_id": 791,
"admin_recipient": "",
"infinity_folder_id": "approved-folder-id",
"variable_map": {
"student_name": "event.certificate_name",
"course_name": "route.course_title",
"completed": "date.completed_long",
"document_number": "sequence.document_number"
}
}
]
Allowed mappings are allowlisted event.* fields, route titles, the bridge document number, completion-date forms, or sanitized constant: values. Arbitrary code, callbacks, and expressions are rejected. An enabled route additionally requires the selected ACG templates to exist, be linked/enabled, and satisfy all required certificate variables.
Before enabling any route, record and explicitly confirm the final Pabbly document number in the bridge setting. The first bridge event reserves the next number; retries keep the reservation and gaps are never reused.
Processing behavior
- A result qualifies only when
requires_grading=noand score is at least its route threshold (80.0 qualifies). - Link events use
classmarker:link:{link_result_id}; group events use the documented test/group/user/start-time key. - Repeated identical delivery is acknowledged without a second certificate.
- If the first accepted delivery was stored but could not be queued, an unchanged redelivery recovers the
receivedevent by queueing it. An event already markedqueuedremains idempotent and is not queued twice. - The matching enabled route is snapshotted when a webhook is accepted. Later route edits cannot redirect an already accepted result; route revisions make intentional configuration changes auditable.
- A changed pre-issuance resend updates the snapshot and may transition a pending/below-threshold event to eligible.
- A changed post-issuance result cannot create another certificate or resend an already successful email. It may require Infinity reconciliation or administrator review.
- ACG receives the canonical source/event key as its independent duplicate guard.
- Certificate, student-email, administrator-email, and Infinity stage states are stored separately. Retryable email/Infinity errors are scheduled with bounded exponential backoff; successful stages are not replayed.
- Unexpected or malformed ACG adapter results fail closed as retryable errors. Infinity transport exceptions, malformed JSON, ambiguous email matches, and searches that exceed the 100-page safety bound fail without creating or updating an uncertain record.
Infinity mapping
The Infinity API base is fixed in code to https://app.startinfinity.com/api/v2; arbitrary external URLs are not configurable. Before any route is enabled, enter validated immutable attribute IDs for:
student_name,student_email,student_notes,score,completedgot_digital_certificateand itsgot_digital_certificate_yes_label
The bridge finds records in the route folder by normalized student email. One match is updated, no match is created with MANUAL UPDATE NEEDED, and multiple matches fail terminally rather than risking an incorrect record. The approved direct Infinity connection test remains required before a real write.
Operations, privacy, and recovery
Use Settings → ClassMarker Bridge Events to view redacted event identifiers and stage status. A retry control appears only for a retryable failed stage and requires an administrator capability plus nonce; its audit entry records the initiating WordPress user ID and UTC time. Event details deliberately omit raw payloads, answers, headers, tokens, download tokens, and PDF paths.
An optional aggregate health panel can be enabled under the bridge settings. It reports only allowlisted runtime/dependency readiness, retention schedule state, status totals, retryable-event count, and event/retry timestamps. Its JSON export requires administrator capability and a nonce. It never includes event rows, identifiers, names, email addresses, payloads, routes, error messages, secrets, or tokens. Disabling it does not affect the operational event ledger or retention.
Terminal event snapshots are retained for 180 days by default (configurable from 30–730) and cleaned in bounded batches. Deactivation and uninstall preserve configuration and event records; no data deletion is automatic.
LearnDash identity and course completion are deliberately deferred to a later release.
Local development
composer install
php vendor/bin/phpunit -c phpunit.xml.dist --do-not-cache-result
php vendor/bin/phpcs
The current test suite covers payload normalization, signature verification, route precedence and validation, eligibility thresholds, variable mapping, queue recovery, retry limits, adapter contracts, and safe Infinity failure modes. Runtime, ClassMarker, SMTP, and Infinity validation require a separately confirmed target and approval.
Additional project commands:
npm.cmd run qa
npm.cmd run lint:fix
npm.cmd run build
npm.cmd run pot
npm run build is a dependency-free release-input preflight. The plugin has no JavaScript or CSS asset source; the preflight fails if an unconfigured asset source directory appears.
Non-production fixture runner
The bridge includes a server-only WP-CLI fixture runner for repeatable route validation without completing a real ClassMarker assessment. It is unavailable by default and always refuses a production WordPress environment.
On the approved staging target only, add this temporary server-side constant outside version control:
define( 'ALYNT_CLASSMARKER_BRIDGE_ENABLE_FIXTURE_RUNNER', true );
Then use a unique, safe result ID and .test identity for an enabled exact route:
wp acmb fixture run --test-id=123 --link-id=456 --link-result-id=789 --percentage=80 --email=acmb-fixture-route-789@example.test --confirm=run-fixture
The command builds the supported ClassMarker link-result schema, signs the exact raw JSON using the configured server secret, confirms that an enabled route matches, and sends it through the bridge's own REST intake. It may generate the route's certificate, simulated notifications, and Infinity update, so it remains a target-specific staging approval gate. Remove the enable constant after the fixture window.
For package-preparation boundaries and the runtime/cutover gates, see the release checklist. Internal scheduler and administration callback names are listed in the hook reference.
Changelog
See CHANGELOG.md for the complete release history.
License
GPL v2 or later. See LICENSE.
Read the full README on GitHub →
Releases
| Tag | Published | Asset | Downloads |
|---|---|---|---|
| v0.1.0 | Jul 26, 2026 | alynt-classmarker-bridge-v0.1.0.zip | 1 |