WP Manifestindependent plugin directory
manifest / integrations / alynt-classmarker-bridge

Alynt ClassMarker Bridge

ClassMarker result bridge for Alynt Certificate Generator.

by nichlasb · github.com/nichlasb/alynt-classmarker-bridge

0stars
1release downloads
0forks

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.zip

Declares 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

  1. Build or obtain an approved release ZIP whose root folder is alynt-classmarker-bridge.
  2. Upload and activate the plugin through WordPress Plugins administration on the confirmed target.
  3. Confirm Alynt Certificate Generator 0.3.0 or later and Action Scheduler are available.
  4. Define provider secrets outside version control.
  5. 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=no and 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 received event by queueing it. An event already marked queued remains 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, completed
  • got_digital_certificate and its got_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

TagPublishedAssetDownloads
v0.1.0 Jul 26, 2026 alynt-classmarker-bridge-v0.1.0.zip 1