Skip to content
Connected Services Framework
Standalone Documents
Markdown

#MAP Onboarding & Testing Process

Connected Services Framework (CSF) — Companion Document — Version 1.0 (Draft)

Maintained by the Telecom Technical Architecture Group (TAG). Referenced from Part 1: Onboarding, Part 1: Governance, and Part 2: Getting Started.


#1. Purpose

This document is the practical handbook for any party preparing to deploy and operate a Managed Access Provider (MAP) on the Connected Services Framework (CSF). It exists to help engineers and project teams achieve a robust, production-ready deployment with minimal friction and maximum confidence.

It serves three distinct audiences working together:

AudienceWhat this document delivers
DevelopersConcrete API behaviours, error responses, signing examples, and a public PKI test harness for round-tripping signatures end-to-end
ImplementersA phased onboarding journey, environment expectations, status-flagging rules, and an exhaustive checklist tying CSF documentation chapters together
TestersA baseline test suite of positive, negative, and edge-case scenarios — covering both CSF transport and the SforB industry protocol — that every new MAP MUST pass before production access is granted

The CSF is intentionally distributed and unlicensed. Trust in the network is earned through demonstrable conformance, not through a central certifying authority. This document is the basis for that demonstration.

Note: This document significantly exceeds the requirements currently set forth by TOTSCo for hub onboarding. The CSF carries no central operator; therefore, the reception of MAPs depends on the meticulous deployment standards of each individual MAP. Extensive baseline testing should be regarded as a safeguard among peers rather than an administrative obstacle. It is anticipated that implementers utilising the CSF will adhere to the guiding principles by conducting comprehensive baseline testing for both the CSF transport and in relation to the specific protocol being implemented. This process will facilitate a profound understanding for each new MAP integrating into the network, enabling them to benefit from the collective efforts of existing MAPs and to achieve the highest level of maturity possible through the completion of all requisite baseline tests.

#2. How to Use This Document

The document is organised as a journey:

  1. Sections 3–5 describe the environment, status model, and traffic-flagging rules you must understand before starting.
  2. Section 6 describes the phased onboarding flow and which tests apply at each phase.
  3. Section 7 is the NowYoYo PKI Test Harness — copy-and-run examples for the area engineers find hardest to get right.
  4. Section 8 is the TAG Baseline Test Suite — every test case a new MAP must pass.
  5. Sections 9–11 cover exit criteria, bilateral testing, and ongoing regression obligations.

Engineers should iterate Sections 3 → 8 in order during build. Implementers and testers should treat Section 8 as the contract that defines "ready for production".

#3. Test Environments

The CSF supports testing in production as a first-class operating mode. There is no need for a separate CSF pre-production/test network. Instead, environments are distinguished by participants' status and by an explicit test flag in every message envelope. This is more efficient for the MAPs because each exchange is peer-to-peer, and so each operator is clear whether the intended message is for test or production. This method allows multiple pseudo networks to coexist to suit specific needs and is ideal for onboarding new MAPs, as the sponsor MAP can ensure that all traffic is treated as a test while it is being evaluated.

#3.1 Environment Types

EnvironmentProviderPurpose
Local
Sandbox
Each MAP
in-house
Unit, integration, and component tests within a single MAP's stack. Typically associated with Development
PKI Verification EndpointNowYoYo (public, free)The hosted DKIM verification harness is documented in Section 7. Used at any stage to debug signing. Other endpoint networks can also be made available if required to help the new team with their development phase.
Virtual Test Host (VTH)Sponsor MAP
(optional)
A simulated counterparty exposing CSF endpoints. Used for early connectivity testing, OAuth, registry and signing tests without involving live MAPs
Test TenantSponsor MAPA real CSF endpoint operated by the Sponsor MAP under TEST status. Used for end-to-end SforB process tests with test CPs
Production with TEST flagAll MAPsFull production network with TEST status applied to the new MAP and/or its CPs. Used for operational validation prior to going live
ProductionAll MAPsFull production network with ACTIVE status applied to all contributing MAPs and/or its CPs.

#3.2 Environment Sequencing

flowchart TB A["Local Sandbox"</br> MAP In-house Testing] --> B["PKI Verification </br>Endpoint"] B --> C["Sponsor MAP </br>Virtual Test Host"] C --> D["Sponsor MAP </br>Test Tenant"] D --> E["Production </br>w/ TEST flag"] E --> F["Production </br>Full Operations"]

Engineers SHOULD progress only when the prior stage is green. Skipping the PKI Verification Endpoint stage is the most common cause of long-running onboarding incidents and so MAP like NowYoYo have created an end point to help others with getting this right.

#3.3 Required CSF Endpoints (under test)

A MAP under test MUST expose all four endpoints over TLS 1.3, even if their content is restricted or test-only:

EndpointPurposeFirst-pass test
OAuth 2.0 token URLIssue access tokens to peer MAPs (Client Credentials flow)Successfully issue a bounded-scope token
Directory API URLServe the MAP's CP Registry as JSONReturn a syntactically valid registry containing only test CPs
Letterbox URL (CSF)Receive signed messages from CSF MAPsAccept a signed test JAM message and return HTTP 202
Service Status URLReport operational stateReturn current state to peers

Best Practice: Even in test, separate the CSF letterbox endpoint from any HMAP letterbox endpoint (e.g. for TOTSCo). This habit avoids accidental cross-network exposure later as HMAP tend to be static and unlikely to change, whereas the CSF interface support versioning and feature releases as standard and is expect to evolve more quickly to support new industry protocols.

#3.4 Sponsor MAP via the CP Registry

The CSF requires Sponsor MAP responsibilities to be distributed equally among all onboarded MAPs using a sequential FIFO rotation. MAPs join the rotation in the order they are onboarded — their position (order) is permanent and immutable. The mechanism supports concurrent sponsorships, which are needed when multiple new MAPs (whether single-CP or traditional multi-CP) are onboarding simultaneously.

This proposal defines a decentralised, self-service mechanism using the CP Registry JSON. No central coordination is needed.

#Design Principles

  • FIFO rotation: MAPs are allocated sponsorships in the order they joined the network. order is assigned at onboarding and never changes.
  • Forward pointer: The token (nextSponsor) is a forward pointer — it identifies who is next, not who is current. The current sponsor is the MAP that most recently updated it. No majority consensus is needed.
  • Advance on acceptance: The token advances when the Sponsor MAP accepts a new engagement, not when it completes one. This keeps the queue moving regardless of how long an individual onboarding takes.
  • Concurrent sponsorships: Each MAP declares a capacity and tracks active engagements. If the designated MAP is busy, the next MAP in the rotation takes the overflow automatically.
  • Self-discoverable: Any MAP — including a prospective new MAP — can find the current Sponsor by reading any onboarded MAP's registry.

#Proposed Solution: sponsorship Object in the MAP Section

Add a sponsorship object to the map section of each MAP's CP Registry.

Idle MAP — no active sponsorships:

{
  "map": {
    "name": "alphaMAP",
    "version": "2026-01-10T09:00:00Z00000",
    "sponsorship": {
      "order": 1,
      "nextSponsor": 2,
      "nextSponsorUpdated": "2026-01-10T09:00:00Z",
      "capacity": 1,
      "active": []
    }
  }
}

MAP with an active sponsorship engagement:

{
  "map": {
    "name": "gammaMAP",
    "version": "2026-06-09T10:00:00Z00000",
    "sponsorship": {
      "order": 3,
      "nextSponsor": 4,
      "nextSponsorUpdated": "2026-06-09T10:00:00Z",
      "capacity": 2,
      "active": [
        {
          "id": "a3f91c2d-7e4b-4f8a-b1c2-9d3e5f6a7b8c",
          "type": "traditional",
          "opened": "2026-06-09T10:00:00Z"
        }
      ]
    }
  }
}

MAP handling two concurrent onboardings (at capacity):

{
  "map": {
    "name": "gammaMAP",
    "version": "2026-06-09T10:05:00Z00000",
    "sponsorship": {
      "order": 3,
      "nextSponsor": 4,
      "nextSponsorUpdated": "2026-06-09T10:05:00Z",
      "capacity": 2,
      "active": [
        {
          "id": "a3f91c2d-7e4b-4f8a-b1c2-9d3e5f6a7b8c",
          "type": "traditional",
          "opened": "2026-06-09T10:00:00Z"
        },
        {
          "id": "b7e22d10-3c9f-4a1d-e5f2-8d4c0b7a3e29",
          "type": "single-cp",
          "opened": "2026-06-09T10:05:00Z"
        }
      ]
    }
  }
}

#Field Definitions

JSON PathDescriptionTypeRequired
map/sponsorshipSponsor rotation metadataObjectYes
map/sponsorship/orderThis MAP's permanent FIFO position in the rotation. Assigned at onboarding; never changes even if other MAPs leave the network — the order of a withdrawn or removed MAP is preserved for historical audit consistency, and the rotation simply skips entries whose map/status is not ACTIVE.IntegerYes
map/sponsorship/nextSponsorThe order number of the MAP designated as the next Sponsor after this MAP. Updated when this MAP accepts a new sponsorship engagement.IntegerYes
map/sponsorship/nextSponsorUpdatedTimestamp when nextSponsor was last updated (ISO 8601 UTC). The MAP with the most recent nextSponsorUpdated is the current active Sponsor.DateTimeYes
map/sponsorship/capacityMaximum concurrent sponsorships this MAP will accept simultaneously. Minimum: 1 (every MAP MUST be willing to take at least one engagement at a time). No upper bound — capped only by the MAP's own operational capacity, which it determines for itself. A MAP that primarily takes single-cp (MAP-of-1) engagements will typically support a higher capacity because those engagements complete faster than traditional multi-CP onboardings.IntegerYes
map/sponsorship/registrationURIOptional. A URI advertising the Sponsor MAP's own onboarding application page — for example a self-service portal, contracted onboarding form, or an HTTPS endpoint accepting a structured registration request. Allows prospective MAPs (and the TAG-published sponsor-registration page) a machine-readable entry point to begin the onboarding conversation. Omit the field if the MAP uses non-electronic registration (e.g. email-only, telephone, or the MAP's general map/contact[] entries). When omitted, prospective MAPs use map/contact[] as the contact channel.URINo
map/sponsorship/active[]Array of currently open sponsorship engagements. Empty array when idle.Object ArrayYes
map/sponsorship/active[]/idUUIDv4 identifier for this engagement, generated by the Sponsor MAP on acceptance.String (UUID)Yes
map/sponsorship/active[]/typeType of candidate: traditional (multi-CP MAP) or single-cp (MAP onboarding with a single CP, e.g. a MAP-of-1 migrating from TOTSCo SforB per Part 1 §7.4). Both types count as 1 against the MAP's capacity — no reduced weighting for single-cp despite the typically faster completion. The equal-weighting rule keeps the rotation fair to small MAPs that operate at capacity: 1: such a MAP should not be expected to take additional engagements simply because some are MAP-of-1s.String (enum)Yes
map/sponsorship/active[]/openedTimestamp when the Sponsor MAP accepted the engagement (ISO 8601 UTC).DateTimeYes
map/sponsorship/active[]/candidateRefOptional. A non-identifying reference assigned by the Sponsor MAP for its own coordination — for example a ticket number from the Sponsor's own onboarding system when the MAP is handling several concurrent engagements. Free-form string; intentionally not standardised across MAPs because the value has no consumer outside the issuing MAP's own back-office systems.StringNo

#How to Determine the Current Sponsor

Any MAP — including a prospective new MAP — can determine who to contact:

  1. Collect all active MAPs' CP Registries (or start with any single MAP's registry)
  2. Read sponsorship.nextSponsor and sponsorship.nextSponsorUpdated from each
  3. The entry with the most recent nextSponsorUpdated is authoritative — that MAP's nextSponsor value is the order of the designated next Sponsor
  4. Check if the designated MAP has capacity: active.length < capacity
  5. If at capacity, follow that MAP's nextSponsor to find the next available MAP in the chain

No consensus algorithm is needed. Only the current Sponsor should be advancing nextSponsor, so the most recently updated entry is inherently authoritative.

#How the Rotation Works

Step 1 — Assignment at onboarding

When a new MAP is onboarded, the Sponsor MAP assigns it the next sequential order number. This is permanent.

Example network (4 active MAPs):

MAPorderStatus
Alpha MAP1ACTIVE
Beta MAP2ACTIVE
Gamma MAP3ACTIVE
Delta MAP4ACTIVE

Step 2 — Accepting a sponsorship

When a candidate contacts the designated Sponsor and the Sponsor accepts:

  1. The Sponsor adds an entry to active[] with a new UUIDv4 id, the type, and opened timestamp
  2. The Sponsor advances nextSponsor using: nextSponsor = (order % totalActiveMAPs) + 1, skipping any non-ACTIVE MAPs
  3. The Sponsor updates nextSponsorUpdated to the current time and publishes the updated registry
  4. All other MAPs detect the change within one polling cycle (up to 5 minutes)

The queue advances at acceptance — not at completion.

Step 3 — Completing a sponsorship

When onboarding completes:

  1. The Sponsor removes the engagement entry from active[]
  2. nextSponsor is not changed — it was already advanced at acceptance

Step 4 — Concurrent onboardings (overflow to next MAP)

If two candidates request sponsorship while Gamma MAP (order 3, capacity 1) is designated:

  • Candidate A arrives. Gamma accepts: adds to active[], sets nextSponsor: 4, updates nextSponsorUpdated
  • Gamma is now at capacity. Candidate B arrives. Delta MAP (order 4) is now designated
  • Delta accepts Candidate B: adds to active[], sets nextSponsor: 1
  • Gamma and Delta run their onboardings in parallel

If Gamma has capacity: 2, it can accept both Candidate A and Candidate B simultaneously, advancing nextSponsor: 4 after the second acceptance.

#Worked Example

Starting state — the most recently updated nextSponsor is Gamma's (from 2026-05-01), pointing to Delta as the designated next Sponsor. Gamma is therefore the current Sponsor.

Alpha MAP:  { order:1, nextSponsor:2, nextSponsorUpdated:"2026-01-10T...", active:[] }
Beta MAP:   { order:2, nextSponsor:3, nextSponsorUpdated:"2026-03-05T...", active:[] }
Gamma MAP:  { order:3, nextSponsor:4, nextSponsorUpdated:"2026-05-01T...", active:[] }  ← current (most recent)
Delta MAP:  { order:4, nextSponsor:1, nextSponsorUpdated:"2025-12-01T...", active:[] }

Scenario: two new MAPs arrive simultaneously

New MAP "Epsilon" (traditional) and New MAP "Zeta" (single-cp) both request sponsorship.

Gamma (capacity:1) accepts Epsilon:

Gamma MAP:  {
  order:3, nextSponsor:4, nextSponsorUpdated:"2026-06-09T10:00:00Z",
  active: [{ id:"abc...", type:"traditional", opened:"2026-06-09T10:00:00Z" }]
}

Delta is now designated. Delta (capacity:1) accepts Zeta:

Delta MAP:  {
  order:4, nextSponsor:1, nextSponsorUpdated:"2026-06-09T10:03:00Z",
  active: [{ id:"def...", type:"single-cp", opened:"2026-06-09T10:03:00Z" }]
}

Both onboardings proceed in parallel. Alpha MAP is designated for the next request.

When Gamma completes Epsilon's onboarding:

Gamma MAP:  { order:3, nextSponsor:4, nextSponsorUpdated:"2026-06-09T10:00:00Z", active:[] }

nextSponsor is unchanged. Epsilon is assigned order: 5. Zeta (when completed) is assigned order: 6.

#Handling Edge Cases

MAP leaves the network or is suspended: When advancing nextSponsor, skip any MAP whose map/status is not ACTIVE until finding the next active MAP.

Designated MAP at capacity: A prospective candidate walks the chain: read the designated MAP's nextSponsor to find the next in line, and so on, until finding a MAP where active.length < capacity.

Disagreement on nextSponsorUpdated: Timestamps are set by the writing MAP — no consensus is needed. If two MAPs somehow update simultaneously (extremely unlikely in a small network), the MAP with the lower order number takes precedence.

Designated MAP receives no candidates for an extended period: There is no maximum hold time on the designated-sponsor position. A MAP that is designated but receives no candidate simply remains designated until a candidate arrives. The rotation is event-driven (advance on acceptance), so an idle period at the head of the rotation has no operational cost and no fairness concern — the next acceptance still advances the queue normally.

Prospective MAP finding the Sponsor before joining: There are two discovery paths. The primary path is the TAG-published sponsor-registration page (URL to be published by the TAG), which lists the current designated Sponsor MAP and their registration contact and is updated whenever the rotation advances. The fallback is the in-band Directory bootstrap: the prospective MAP accesses any existing MAP's CP Registry, finds the entry with the most recent nextSponsorUpdated, checks capacity, and contacts that MAP's map/contact[] entries. The TAG page is the recommended path for human candidates; the Directory bootstrap remains available for automated tooling and as a fallback if the page is briefly unreachable.

#Why This Works

  • FIFO enforced: order never changes. The formula (order % n) + 1 is deterministic and wraps correctly
  • No consensus needed: The most recent nextSponsorUpdated is always authoritative — only the current Sponsor should update the pointer
  • Concurrent onboarding: active[] + capacity handle multiple simultaneous engagements transparently; overflow falls naturally to the next MAP in the chain
  • Fast queue: Advancing on acceptance means a long onboarding never blocks the rotation
  • Self-discoverable: Any MAP can find the current Sponsor and any candidate can find them before even joining
  • Auditable: active[]/opened and nextSponsorUpdated provide a clear operational history

#Design Rules

The rules below govern the operation of the sponsorship rotation and the optional-field semantics. They complement the Field Definitions and the Edge Cases handling above, and apply to every MAP that publishes a CP Registry on the CSF.

RuleStatement
Capacity boundsThe minimum permitted capacity is 1 — every onboarded MAP MUST be willing to take at least one engagement at a time. There is no upper bound on capacity; a MAP sets the value to reflect its own operational throughput. A MAP that primarily takes single-cp (MAP-of-1) engagements MAY support a higher capacity because those engagements complete faster than traditional multi-CP onboardings.
Engagement weightingA traditional engagement and a single-cp engagement each count as 1 against the MAP's capacity. No reduced weighting is applied to single-cp. The equal-weighting rule protects small MAPs operating at capacity: 1: such a MAP is not expected to take additional engagements simply because some are MAP-of-1s.
order persistenceA MAP's order is permanent. When a MAP withdraws or its map/status becomes non-ACTIVE, its order is retained for historical audit consistency and is not reassigned. The rotation skips entries whose map/status is not ACTIVE.
Designated-position hold timeThere is no maximum hold time on the designated-sponsor position. A designated MAP that receives no candidate remains designated until a candidate arrives. The rotation is event-driven — advance occurs on acceptance — so an idle period has no operational cost and no fairness implication.
candidateRefactive[]/candidateRef is OPTIONAL. When present, it carries a non-identifying reference assigned by the Sponsor MAP for its own coordination — for example a ticket number from the Sponsor's internal onboarding system. The field is free-form and is not standardised across MAPs; consumers outside the issuing MAP MUST NOT rely on its semantics.
sponsorship.registrationURIsponsorship.registrationURI is OPTIONAL. When present, it advertises the Sponsor MAP's own onboarding application page (self-service portal, contracted form, or structured registration endpoint), enabling prospective MAPs and the TAG-published sponsor-registration page to deep-link into that MAP's onboarding flow. A MAP that uses non-electronic registration (email, telephone, ticketing) omits the field; prospective MAPs then use the MAP's map/contact[] entries as the contact channel.

The sponsorship object — its required fields, its two optional fields, and the rules in this section — forms part of the CP Registry API specification and is implemented by every MAP on the CSF.


#4. Status Model and JSON Status Values

The CSF uses two distinct status fields. Both are JSON string values, both are observable to peer MAPs, and both have specific consequences during testing.

#4.1 Per-CP Status (CP Registry — processSupport[].status)

Defined in the Directory API (see Part 2 §5).

  "processSupport": [
    { "process": "GPLB", "status": "ACTIVE" }
    ]
ValueMeaningAppears in CP-facing Directory?Messages allowed?
ACTIVECP is live and available for production switchingYesYes — production messages
TESTCP is in onboarding or testingNo — MUST be excluded from CP-facing DirectoriesYes, but every message MUST be flagged as test in the JAM audit envelope
SUSPENDCP is suspended (administration, dispute, planned migration)No — MUST be excluded from CP-facing DirectoriesOutbound only — in-flight orders may transfer away; no new Switch Matches accepted

Best Practise requires the Sponsor MAP to ensure that no messages from a new MAP are treated as Production at ever each stage until they sign them off for completion.

#4.2 Per-MAP Status (CP Registry — map.status, optional)

Defined in the Directory API (see Part 2 §5). Used during MAP onboarding to mark an entire MAP as in test. When map.status is TEST, every CP under that MAP is treated as test regardless of its own per-CP status.

  "map": {
    "name": "newMap",
    "status": "TEST",
    "version": "2026-04-28T10:30:00Z00000",
    ...
  }
ValueMeaningEffect on receiving MAPs
ACTIVE (default if absent)MAP is fully operationalNormal operations
TESTMAP is onboardingEstablish connectivity and pull registries, but exclude all of this MAP's CPs from the CP-facing Directory
SUSPENDMAP is suspended (incident, dispute, administration)Retain the registry data, mark all entries unavailable, queue messages for retry

#4.3 Process Status Casing — Compatibility Note

Existing OTS implementations have used both Active and ACTIVE casings. CSF MAPs MUST emit upper-case status values (ACTIVE, TEST, SUSPEND) and SHOULD parse case-insensitively for backward compatibility with TOTSCo-sourced data.

#5. Test Traffic Flagging in the JAM Envelope

The envelope schema and the optional nature of auditData are defined in Message API (Letterbox) §4.3 — Message Structure; this section only specifies the CSF-wide conventions for populating that field during onboarding and bilateral testing.

Every message originated by, or routed on behalf of, a TEST-status CP or a TEST-status MAP MUST carry an explicit test marker in the JAM envelope's auditData:

{
  "envelope": {
    "source": { "type": "RCPID", "identity": "...", "correlationID": "..." },
    "destination": { "type": "RCPID", "identity": "..." },
    "routingID": "businessSwitchMatchRequest",
    "auditData": [
      { "name": "test", "value": "true" }
    ]
  }
}

Rules of behaviour during onboarding:

  • The originating MAP sets auditData.test=true whenever it sends on behalf of a CP whose registry status is TEST or whose own MAP status is TEST.
  • The originating MAP overrides any contradictory flag set by the CP. The CP cannot opt out.
  • The receiving MAP MUST preserve auditData.test=true end-to-end and MUST NOT clear it during forwarding through HMAPs.
  • All test traffic MUST be excluded from production reporting, KPIs, and customer notifications.
  • A MAP MUST NOT change a CP's status from TEST to ACTIVE until the exit criteria in Section 9 have been satisfied.

Note: The MAP-overrides-CP rule is what makes "testing in production" safe. Without it, a misconfigured CP could silently inject test traffic into live counterparties.

#5.1 Telemetry Exclusion

Industry-wide telemetry collection (defined in Part 1 §10.6.7 — Anti-Competitive Safeguards) is concerned only with production traffic between ACTIVE participants. To prevent onboarding, bilateral, or soak-test activity from polluting industry KPIs, every MAP MUST exclude a message from telemetry statistics if any of the following are true:

ConditionSource of truth
Originating MAP carries map.status = TEST or SUSPENDCP Registry — map.status
Destination MAP carries map.status = TEST or SUSPENDCP Registry — map.status
Source CP carries processSupport[].status = TEST or SUSPEND for the message's processCP Registry — list[].identity[].processSupport[].status
Destination CP carries processSupport[].status = TEST or SUSPEND for the message's processCP Registry — list[].identity[].processSupport[].status
The JAM envelope's auditData contains { "name": "test", "value": "true" }JAM envelope on the message itself

Any one of these conditions is sufficient to exclude the message from production telemetry. The auditData.test=true flag in particular is the belt-and-braces signal — it is set by the originating MAP regardless of registry state and survives end-to-end through any HMAP, so a receiving MAP can reliably exclude the message even if its registry view is momentarily stale.

Best Practice: Test-flagged messages MAY still be retained for audit and onboarding evidence, but MUST be held in a logically separate store from the production telemetry corpus and clearly labelled as test data. They MUST NOT be aggregated into the figures contributed to the TAG's annual industry summary.

#Sequencing of session and attempt metadata in auditData

The auditData array is also the place where session-level metadata SHOULD be carried so that production telemetry (once a message is in scope) can be counted by customer journey rather than by individual request. A SforB match journey often spans several businessSwitchMatchRequest attempts as the GRCP corrects the data; counting each as an independent event structurally inflates the industry's failure rate. The CSF accommodates a three-layer sequencing scheme — GRCP-originated session IDs (Layer 1), LRCP echo and enrichment (Layer 2), and MAP-mediated session inference (Layer 3) — using only the existing extensibility of auditData. No envelope, JAM, or SforB Message Spec change is required.

Recommended auditData field-name prefixes (full registry in REVIEW.md §6.5):

PrefixOwnerExamples
match*GRCP-originated (Layer 1)matchSessionID, matchAttemptSeq, matchAttemptReason
serviceMatch*GRCP-originated, service-match phaseserviceMatchAttemptSeq, serviceMatchAttemptReason
lrcp*LRCP-originated (Layer 2)lrcpMatchAttemptCount
map*MAP-originated (Layer 3)mapSessionID, mapAttemptSeq, mapRetryFlag, mapDataChangeFlag, mapConcurrentGrcpCount

The auditData.test=true flag and any session/attempt fields are independent and complementary: the test flag governs whether a message is in production telemetry at all (this section); the session fields govern how the in-scope messages are aggregated into per-session KPIs (REVIEW.md §6.5). MAPs implementing Layer 3 unilaterally SHOULD use the field-name prefixes above to avoid colliding with the eventual industry-standard names.

:link: Related: The full proposal — including session-success-rate definitions, GRCP/LRCP/MAP responsibilities, the field-name registry, the SOR boundary rules, privacy treatment of matchAttemptReason, and the four-phase implementation strategy — lives in REVIEW.md §6.5 — Match Session Sequencing. The TAG is currently considering whether to advance it as a TOTSCo IPG bulletin or a GPLB-SG SforB Message Spec revision; until adopted, MAPs MAY implement Layer 3 unilaterally as a value-add, using the prefixes above.

#6. Onboarding Phases

The five phases below align with Part 1 §7.2.2. This section defines the gates between them.

#Phase 1 — Sponsor Selection

Entry criterionExit gate
MAP candidate has formally requested onboarding via the TAG-published registration page (URL to be published by the TAG) or via the Directory bootstrap mechanism (§3.4)Sponsor MAP assigned (next Sponsor) and onboarding agreement signed

Best Practice: Sponsor MAPs SHOULD publish, or share on first contact, a short summary of their registration process — including what evidence they will request, how they accept it, the channel used to share secrets, and the expected turnaround time. This makes the experience predictable for new entrants and reinforces the CSF principle that onboarding should be operational in days, not months.

Sponsor fee — free by default; capped when significant support is needed. Sponsor MAPs are not expected to charge for onboarding. There is an option to charge a fee when significant time and effort is required, capped at £3,000 + VAT for a traditional (multi-CP) engagement or £350 + VAT for a single-cp (MAP-of-1) engagement. The option exists because the CSF expects the Sponsor MAP to apply a quality gate — all checks and baseline testing complete to TAG standards. Fees are charged once per MAP regardless of how many protocols it carries. See Part 1 §7.2.1 and Part 1 §7.4.5 for the full fee model.

#Phase 2 — Initial Assessment & Registration

Entry criterionExit gate
Sponsor MAP confirmed; capability self-assessment submittedSponsor sign-off on infrastructure plan; OAuth credentials minted with provisional/restricted scope; map.status=TEST, no CPs published yet

Once the TAG has provided the new MAP candidate with the contact details of the assigned Sponsor MAP, the Sponsor MAP may require the candidate to complete its own formal registration process before any technical engagement begins. This is a normal commercial step and serves two purposes:

  1. Business verification — confirming the candidate is a legitimate trading entity (company registration, VAT, ICO registration, trading history, fraud and credit checks where applicable). This complements the wider due-diligence expectation already captured in the Risk Register (see Part 2 §9) and in REVIEW.md §4.
  2. Secure information exchange — providing a controlled channel through which sensitive material can be shared in both directions, including (but not limited to):
    • URL endpoints (OAuth token URL, Directory API URL, Letterbox URL, Service Status URL)
    • OAuth client IDs and client secrets
    • API keys, where used between the MAPs
    • PKI public-key fingerprints and DNS verification details
    • Operational and technical contact lists for incident handling

Each Sponsor MAP may have a different method for engaging at this stage — for example a self-service portal, a contracted onboarding form, a secure file-exchange channel, or a structured email-based exchange protected by encryption. These differences are expected and are not standardised by the TAG, since the registration process sits outside the technical scope of the CSF and reflects each Sponsor MAP's own commercial, legal, and security policies.

Note: The candidate MAP should treat the Sponsor MAP's registration process as the gateway to all subsequent phases. Until it is complete, no OAuth credentials will be issued, no test endpoints will be shared, and no technical onboarding work can begin. Candidate MAPs are encouraged to ask the Sponsor MAP for a clear list of registration requirements at first contact, so that any documentation or evidence (e.g. company registration documents, ICO certificates, security policies) can be prepared in parallel.

#Phase 3 — Integration & Testing (the core build phase)

This is the phase in which most of Section 8 is exercised.

Sub-phaseTests requiredOutcome
3a — Connectivity§8.1 (TLS), §8.2 (OAuth)Bidirectional authenticated connectivity with the Sponsor MAP
3b — Registry§8.3 (Directory API)New MAP can publish, and consume from, valid CP Registries
3c — PKI§8.4 (DKIM) and §7 (PKI Harness)Round-trip signing and verification proven against the public NowYoYo verification endpoint
3d — Letterbox§8.5 (Letterbox API)End-to-end JAM message exchange with HTTP 202 acknowledgements
3e — Industry process§8.6 (SforB)Switch Match, Switch Order, and failure handling flows pass against test CPs

Exit gate: All baseline tests in §8.1 to §8.6 pass against the Sponsor MAP. Provisional access is upgraded to test scope; obfuscated/test-only registry data may now be exchanged.

#Phase 4 — Operational Validation

Entry criterionExit gate
Phase 3 sign-off§8.7 (retry & delivery policy), §8.8 (conflict resolution), §8.9 (security/negative) all pass; SLA evidence collected over a continuous 72-hour soak; bilateral connectivity tests with at least 50% of other MAPs completed

MAP-of-1 variant (single-cp engagements). For a candidate onboarding as a MAP of 1 — tracked as type: "single-cp" in sponsorship.active[] (see §3.4) — the Phase 4 exit gate is modified per Part 1 §7.4.9:

  • 72-hour soak waived — a single-CP MAP has no meaningful production-scale traffic to soak, and the CP carries the operational risk directly.
  • Bilateral ≥50% waived — required only with the MAPs / HMAPs (including TOTSCo as HMAP) the candidate intends to exchange with at go-live.
  • §8.7 / §8.8 / §8.9 — required, unchanged.
  • §8.10 V-02 – V-05 — required; V-01 (72-hour soak) — waived.

The Sponsor MAP records the waivers in the Phase 5 approval record.

#Phase 5 — Full Operations

Entry criterionExit gate
Phase 4 sign-offSponsor MAP issues formal approval; map.status removed (or set to ACTIVE); first CP transitioned from TEST to ACTIVE only after passing the per-CP exit checks; TAG notified

#7. PKI Test Harness (NowYoYo)

PKI/DKIM signing is consistently the area that takes the longest to get right. To shorten that learning curve, NowYoYo Limited operates a free, public verification harness. It accepts CSF-formatted POSTs, verifies the signature using the published DNS keys, and either logs every step of the verification (the .txt endpoint) or returns a production-style response (the .json endpoint).

The harness uses two test CPs whose public keys live under gplb-test.nowyoyo.net:

RCPIDAlgorithm
809b6e65-a6e7-40f6-8b52-04dd65b6fce1RSA-SHA256 (3072-bit)
a73f0493-fd1f-4b5b-826c-1d6243d43365Ed25519-SHA256

Use the harness early and often. Most onboarding incidents are caught by the harness within minutes; missing them locally results in days of debugging against a live Sponsor MAP.

#Verification Testing Endpoint

There are 2 endpoints set up for testing.

https://sec-key-test.csf.nowyoyo.net/csf/T/letterbox/2.0/post.txt

Used in 1st-stage development. Success returns 200, failure gives 403. The response body is a timed text log of all the detailed steps that the system used to verify the message. In the case of failure this should be valuable in identifying where things are going wrong.

https://sec-key-test.csf.nowyoyo.net/csf/T/letterbox/2.0/post.json

Used to submit messages to a production-like CSF endpoint. The errors are formatted in the same way as production CSF messages, success is 204, failure is 403. No further processing on the messages is performed.

Note: These endpoints accept signed POST requests only. They are intentionally not browsable in a web browser — wrap them as code in any reference and use the curl examples below to exercise them.

#Examples

#RSA — Success (200) at the .txt endpoint

curl -X "POST" -i "https://sec-key-test.csf.nowyoyo.net/csf/T/letterbox/2.0/post.txt" \
     -H 'X-CSF-SIGNATURE-DATESTAMP: 202412121340391' \
     -H 'X-CSF-SIGNATURE: a=rsa-sha256; q=dns/txt; c=simple/simple; s=809b6e65-a6e7-40f6-8b52-04dd65b6fce1; d=gplb-test.nowyoyo.net; v=1; h=X-CSF-SIGNATURE-DATESTAMP; bh=LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=; b=h3YO8jlz2Gup/szefoH3RxO46IJmnk+8GJ4pxxRyfh+UJ0mZym64o0izGNG1x3GlmMiuHfZc5xP9xIANP+OIajX1udhTGuZkSfUU3L4MrvT/PYkAEfIMm7lFeCWPPgOr0bNIVjNVBrB41akN1iwcQjK0gD+/nUGLVXihdOFCj0xSRIypLPKPxXsZED6sfiBSlEiZcGsF9FCiV5OQQAwRoRhFQE4cnn00QE/S3NoN8/lX7i8OXIaTab6p/HCMIjirM6VBKrc+O0HEdekXn1DP7s0cwp/LkeXTWVloj+vkZREHqNkdIZGeab7N8vFgLrXmYJoR2LM+LQtqhq01KRVSY4hDWdbjD4cSJWkV9P/GWelFiuSNtYmWqTcrT0kqN7pMwVq1Ht4iuhrxH7h2hnCjYG12bEYiHPmarO+JJCPx5RawO0t2BRBxNfEXbp6boTQzZ//MrdW4mUDEyCQhs6s/OHd5x14Z2DJUTYRhugLwGkwSaVjt44y6X9RnB5d9j/Py;' \
     -d "hello"
HTTP/1.1 200
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
Content-Type: application/json
Content-Length: 5818
Date: Tue, 09 Sep 2025 17:07:39 GMT

0ms : Request Received
0ms : Checking Initial CSF MandatoryHeaders [X-CSF-SIGNATURE, X-CSF-SIGNATURE-DATESTAMP]
0ms : Parsing Header Signature: a=rsa-sha256; q=dns/txt; c=simple/simple; s=809b6e65-a6e7-40f6-8b52-04dd65b6fce1; d=gplb-test.nowyoyo.net; v=1; h=X-CSF-SIGNATURE-DATESTAMP; bh=LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=; b=h3YO8jlz2Gup/szefoH3RxO46IJmnk+8GJ4pxxRyfh+UJ0mZym64o0izGNG1x3GlmMiuHfZc5xP9xIANP+OIajX1udhTGuZkSfUU3L4MrvT/PYkAEfIMm7lFeCWPPgOr0bNIVjNVBrB41akN1iwcQjK0gD+/nUGLVXihdOFCj0xSRIypLPKPxXsZED6sfiBSlEiZcGsF9FCiV5OQQAwRoRhFQE4cnn00QE/S3NoN8/lX7i8OXIaTab6p/HCMIjirM6VBKrc+O0HEdekXn1DP7s0cwp/LkeXTWVloj+vkZREHqNkdIZGeab7N8vFgLrXmYJoR2LM+LQtqhq01KRVSY4hDWdbjD4cSJWkV9P/GWelFiuSNtYmWqTcrT0kqN7pMwVq1Ht4iuhrxH7h2hnCjYG12bEYiHPmarO+JJCPx5RawO0t2BRBxNfEXbp6boTQzZ//MrdW4mUDEyCQhs6s/OHd5x14Z2DJUTYRhugLwGkwSaVjt44y6X9RnB5d9j/Py
0ms : Tags present are :
	a : rsa-sha256
	q : dns/txt
	c : simple/simple
	s : 809b6e65-a6e7-40f6-8b52-04dd65b6fce1
	d : gplb-test.nowyoyo.net
	v : 1
	h : X-CSF-SIGNATURE-DATESTAMP
	bh : LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=
	b : h3YO8jlz2Gup/szefoH3RxO46IJmnk+8GJ4pxxRyfh+UJ0mZym64o0izGNG1x3GlmMiuHfZc5xP9xIANP+OIajX1udhTGuZkSfUU3L4MrvT/PYkAEfIMm7lFeCWPPgOr0bNIVjNVBrB41akN1iwcQjK0gD+/nUGLVXihdOFCj0xSRIypLPKPxXsZED6sfiBSlEiZcGsF9FCiV5OQQAwRoRhFQE4cnn00QE/S3NoN8/lX7i8OXIaTab6p/HCMIjirM6VBKrc+O0HEdekXn1DP7s0cwp/LkeXTWVloj+vkZREHqNkdIZGeab7N8vFgLrXmYJoR2LM+LQtqhq01KRVSY4hDWdbjD4cSJWkV9P/GWelFiuSNtYmWqTcrT0kqN7pMwVq1Ht4iuhrxH7h2hnCjYG12bEYiHPmarO+JJCPx5RawO0t2BRBxNfEXbp6boTQzZ//MrdW4mUDEyCQhs6s/OHd5x14Z2DJUTYRhugLwGkwSaVjt44y6X9RnB5d9j/Py
0ms : Using the following Domain for Public Key Lookup: 809b6e65-a6e7-40f6-8b52-04dd65b6fce1._domainkey.gplb-test.nowyoyo.net
0ms : Received the following records:
	k=rsa; p=MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA6/RekuGAsET2Ir8E4K5BU74MBXebo7QwrOnSGirtNgRLDcwP2R84Eeum9BLxZtiumUrmO6ja7DrteSZhzOyC09KM3b22zV43bm7nv/FF40+KpTDQ/FZ32W838UIZGgHOyICk37hlPvnGrVPg7rT6HAKsaj5W0zbvO5vKDUOz2r3bvrWo79YSxtVfGQ1pHhhZYV2dL1C0uiADbz5L1Cu0qCBjqGJQbYq4ALdaKTnAm2NG/N9W2i3IWJUQIyiTr11qtBrrR8sj2YhYeph6GTsOXggpAWZnEHZz+LKzD4roVqUxkByLWRLB2I5Ju/xPPtTjCDakfvGlsiWl5BMAhn79p0mW6hzcA4DT7Iz+nSV8K3XMU4rxs0AcaafcBsKREmfbGz4K3ycWHZ/Yrxb+PqYHy7NSK76mHsAjuU8nuKYtZGIYHwT4friS2R3HtXJ+olLmdZ+XnkpdvEGDb+ZVv/G9vYp020LZQBBd0hp1Ez8a/3F7jRt8xUJ0ljstWjbq+m3bAgMBAAE=
0ms : Have Public Key of RSA type and X.509 format.
0ms : RSA Key length is determined as 3072 bits.
0ms : Body is 5 bytes.
	[104, 101, 108, 108, 111]
0ms : Hashing Algorithm is specified as : SHA256
0ms : SHA256Hash of 5 byte body is : LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=
0ms : Calculated hash and Signature hash are a match
0ms : Starting Signature Generation
0ms : h tag requests the following fields for the signature: [X-CSF-SIGNATURE-DATESTAMP]
0ms : Header 'X-CSF-SIGNATURE-DATESTAMP' contains 202412121340391
	Adding Header Value : [50, 48, 50, 52, 49, 50, 49, 50, 49, 51, 52, 48, 51, 57, 49]
	Adding CRLF : [13, 10]
0ms : Using the following as the signature line with an empty b tag
	a=rsa-sha256; q=dns/txt; c=simple/simple; s=809b6e65-a6e7-40f6-8b52-04dd65b6fce1; d=gplb-test.nowyoyo.net; v=1; h=X-CSF-SIGNATURE-DATESTAMP; bh=LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=; b=;
0ms : Prepending 'DKIM-Signature: '
	DKIM-Signature: a=rsa-sha256; q=dns/txt; c=simple/simple; s=809b6e65-a6e7-40f6-8b52-04dd65b6fce1; d=gplb-test.nowyoyo.net; v=1; h=X-CSF-SIGNATURE-DATESTAMP; bh=LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=; b=;
0ms : Proceeding with Signature Generation and Verification
1ms : Signature verification successful!
1ms : Result = Pass. Well done!

#RSA — Success (204) at the .json endpoint

curl -X "POST" -i "https://sec-key-test.csf.nowyoyo.net/csf/T/letterbox/2.0/post.json" \
     -H 'X-CSF-SIGNATURE-DATESTAMP: 202412121340391' \
     -H 'X-CSF-SIGNATURE: a=rsa-sha256; q=dns/txt; c=simple/simple; s=809b6e65-a6e7-40f6-8b52-04dd65b6fce1; d=gplb-test.nowyoyo.net; v=1; h=X-CSF-SIGNATURE-DATESTAMP; bh=LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=; b=h3YO8jlz2Gup/szefoH3RxO46IJmnk+8GJ4pxxRyfh+UJ0mZym64o0izGNG1x3GlmMiuHfZc5xP9xIANP+OIajX1udhTGuZkSfUU3L4MrvT/PYkAEfIMm7lFeCWPPgOr0bNIVjNVBrB41akN1iwcQjK0gD+/nUGLVXihdOFCj0xSRIypLPKPxXsZED6sfiBSlEiZcGsF9FCiV5OQQAwRoRhFQE4cnn00QE/S3NoN8/lX7i8OXIaTab6p/HCMIjirM6VBKrc+O0HEdekXn1DP7s0cwp/LkeXTWVloj+vkZREHqNkdIZGeab7N8vFgLrXmYJoR2LM+LQtqhq01KRVSY4hDWdbjD4cSJWkV9P/GWelFiuSNtYmWqTcrT0kqN7pMwVq1Ht4iuhrxH7h2hnCjYG12bEYiHPmarO+JJCPx5RawO0t2BRBxNfEXbp6boTQzZ//MrdW4mUDEyCQhs6s/OHd5x14Z2DJUTYRhugLwGkwSaVjt44y6X9RnB5d9j/Py;' \
     -d "hello"
HTTP/1.1 204
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
Date: Tue, 09 Sep 2025 16:41:31 GMT

#RSA — Failure (403, body hash mismatch) at the .json endpoint

curl -X "POST" -i "https://sec-key-test.csf.nowyoyo.net/csf/T/letterbox/2.0/post.json" \
     -H 'X-CSF-SIGNATURE-DATESTAMP: 202412121340391' \
     -H 'X-CSF-SIGNATURE: a=rsa-sha256; q=dns/txt; c=simple/simple; s=809b6e65-a6e7-40f6-8b52-04dd65b6fce1; d=gplb-test.nowyoyo.net; v=1; h=X-CSF-SIGNATURE-DATESTAMP; bh=LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=; b=h3YO8jlz2Gup/szefoH3RxO46IJmnk+8GJ4pxxRyfh+UJ0mZym64o0izGNG1x3GlmMiuHfZc5xP9xIANP+OIajX1udhTGuZkSfUU3L4MrvT/PYkAEfIMm7lFeCWPPgOr0bNIVjNVBrB41akN1iwcQjK0gD+/nUGLVXihdOFCj0xSRIypLPKPxXsZED6sfiBSlEiZcGsF9FCiV5OQQAwRoRhFQE4cnn00QE/S3NoN8/lX7i8OXIaTab6p/HCMIjirM6VBKrc+O0HEdekXn1DP7s0cwp/LkeXTWVloj+vkZREHqNkdIZGeab7N8vFgLrXmYJoR2LM+LQtqhq01KRVSY4hDWdbjD4cSJWkV9P/GWelFiuSNtYmWqTcrT0kqN7pMwVq1Ht4iuhrxH7h2hnCjYG12bEYiHPmarO+JJCPx5RawO0t2BRBxNfEXbp6boTQzZ//MrdW4mUDEyCQhs6s/OHd5x14Z2DJUTYRhugLwGkwSaVjt44y6X9RnB5d9j/Py;' \
     -d "hellox"
{
  "errorText": "Body hash mismatch, calculated Gmu6Qx+5NH5pfydVjH9xKhOJfJX82h5QuFOpeF1zLTc= and from signature LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=",
  "errorCode": "8101"
}

#Ed25519 — Success (200) at the .txt endpoint

curl -X "POST" "https://sec-key-test.csf.nowyoyo.net/csf/T/letterbox/2.0/post.txt" \
     -H 'X-CSF-SIGNATURE-DATESTAMP: 202412121340391' \
     -H 'X-CSF-SIGNATURE: a=ed25519-sha256; q=dns/txt; c=simple/simple; s=a73f0493-fd1f-4b5b-826c-1d6243d43365; d=gplb-test.nowyoyo.net; v=1; h=X-CSF-SIGNATURE-DATESTAMP; bh=LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=; b=LiRX0EqzkcSD4nZwRHlPhtQge4vp3Kssh9Z/Ov4dAl3PHmrhOthDTMs3RoTUSnjPgweoY+wXsCDOPiOGcqAxCQ==;' \
     -d "hello"
0ms : Request Received
0ms : Checking Initial CSF MandatoryHeaders [X-CSF-SIGNATURE, X-CSF-SIGNATURE-DATESTAMP]
0ms : Parsing Header Signature: a=ed25519-sha256; q=dns/txt; c=simple/simple; s=a73f0493-fd1f-4b5b-826c-1d6243d43365; d=gplb-test.nowyoyo.net; v=1; h=X-CSF-SIGNATURE-DATESTAMP; bh=LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=; b=LiRX0EqzkcSD4nZwRHlPhtQge4vp3Kssh9Z/Ov4dAl3PHmrhOthDTMs3RoTUSnjPgweoY+wXsCDOPiOGcqAxCQ==
0ms : Using the following Domain for Public Key Lookup: a73f0493-fd1f-4b5b-826c-1d6243d43365._domainkey.gplb-test.nowyoyo.net
0ms : Received the following records:
	k=ed25519; p=MCowBQYDK2VwAyEAqiCrokPReIFI1h4Jdp7RCRRVQ4Fltdyn429O4H2jYog=
0ms : Have Public Key of EdDSA type and X.509 format.
0ms : EdRSA Key length is 32 bytes
0ms : Body is 5 bytes.
0ms : SHA256Hash of 5 byte body is : LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=
0ms : Calculated hash and Signature hash are a match
0ms : Proceeding with Signature Generation and Verification
1ms : Signature verification successful!
1ms : Result = Pass. Well done!

#Ed25519 — Failure (403, body hash) at the .txt endpoint

curl -X "POST" "https://sec-key-test.csf.nowyoyo.net/csf/T/letterbox/2.0/post.txt" \
     -H 'X-CSF-SIGNATURE-DATESTAMP: 202412121340391' \
     -H 'X-CSF-SIGNATURE: a=ed25519-sha256; q=dns/txt; c=simple/simple; s=a73f0493-fd1f-4b5b-826c-1d6243d43365; d=gplb-test.nowyoyo.net; v=1; h=X-CSF-SIGNATURE-DATESTAMP; bh=LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=; b=LiRX0EqzkcSD4nZwRHlPhtQge4vp3Kssh9Z/Ov4dAl3PHmrhOthDTMs3RoTUSnjPgweoY+wXsCDOPiOGcqAxCQ==;' \
     -d "hellox"
0ms : Request Received
...
44ms : Have Public Key of EdDSA type and X.509 format.
44ms : EdRSA Key length is 32 bytes
45ms : Body is 6 bytes.
45ms : SHA256Hash of 6 byte body is : Gmu6Qx+5NH5pfydVjH9xKhOJfJX82h5QuFOpeF1zLTc=
45ms : Calculated hash and Signature hash (bh) are **not** a match
	Calculated hash is Gmu6Qx+5NH5pfydVjH9xKhOJfJX82h5QuFOpeF1zLTc=
	Signature hash (bh) is LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=
53ms : Result = Fail.

#Ed25519 — Failure (403, signature mismatch via altered datestamp) at the .txt endpoint

curl -X "POST" "https://sec-key-test.csf.nowyoyo.net/csf/T/letterbox/2.0/post.txt" \
     -H 'X-CSF-SIGNATURE-DATESTAMP: 202412121340392' \
     -H 'X-CSF-SIGNATURE: a=ed25519-sha256; q=dns/txt; c=simple/simple; s=a73f0493-fd1f-4b5b-826c-1d6243d43365; d=gplb-test.nowyoyo.net; v=1; h=X-CSF-SIGNATURE-DATESTAMP; bh=LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=; b=LiRX0EqzkcSD4nZwRHlPhtQge4vp3Kssh9Z/Ov4dAl3PHmrhOthDTMs3RoTUSnjPgweoY+wXsCDOPiOGcqAxCQ==;' \
     -d "hello"
0ms : Request Received
...
1ms : Calculated hash and Signature hash are a match
1ms : Starting Signature Generation
1ms : h tag requests the following fields for the signature: [X-CSF-SIGNATURE-DATESTAMP]
1ms : Header 'X-CSF-SIGNATURE-DATESTAMP' contains 202412121340392
1ms : Proceeding with Signature Generation and Verification
3ms : Signature verification has failed!
5ms : Result = Fail.

Note: The signature failure example above demonstrates replay-attack protection in action. Changing only the X-CSF-SIGNATURE-DATESTAMP (without re-signing) breaks verification, exactly as the CSF requires.

#8. TAG Baseline Test Suite

The TAG mandates the following baseline tests for every new MAP. Each test has an outcome classification:

  • P — Positive (the happy path; behaviour MUST succeed)
  • N — Negative (the system MUST reject and return the specified error)
  • E — Edge case (boundary, race, or non-obvious behaviour the system MUST handle correctly)

This suite is significantly broader than the current TOTSCo onboarding because:

  1. There is no central validator — every MAP must independently demonstrate correctness.
  2. The CSF currently combines two industry processes (CSF transport and SforB business process), and tests must cover the interaction between them.
  3. PKI verification is the principal trust mechanism — it must be exercised exhaustively.
  4. Conflict resolution during CP-MAP transitions is unique to the CSF and needs explicit coverage.

#Scope and Extensibility — Other Industry Protocols

While this document currently codifies baseline tests for the CSF transport itself (Sections 8.1 to 8.5, 8.7 to 8.10) and the SforB industry process (Section 8.6), the CSF transport is process-agnostic by design. Other industry protocols that ride over the same transport — for example OTS Port (number porting), future bilateral notification flows, or any new process the TAG agrees to host — are equally in scope of this onboarding regime.

As each additional protocol is brought onto the CSF, the TAG MUST add a corresponding §8.x baseline test set for that protocol, alongside §8.6 (SforB). MAPs and CPs that elect to support that protocol are then required to pass the relevant §8.x set as part of their onboarding (or as a delta if they are already onboarded for other processes). MAPs that do not support the protocol are not required to run those tests.

This means the onboarding suite is intentionally extensible at any point:

  • New protocol → new §8.x section, with its own positive, negative, and edge-case tests.
  • Operational learning on an existing protocol → new test cases appended to that protocol's section, with the date of TAG adoption recorded.
  • Cross-protocol interactions (e.g. a CP supporting both SforB and OTS Port simultaneously, or a CP transitioning between processes) → tests added to the most relevant section, with cross-references where useful.

Note: A MAP supporting multiple industry protocols on the CSF MUST evidence onboarding for each protocol it carries. Sponsor MAPs SHOULD verify this scope explicitly during Phase 1, so the candidate is clear which §8.x sets apply to its proposed footprint. See REVIEW.md §1.9 for the open action covering ongoing baseline expansion.

#8.1 Connectivity & Transport (TLS 1.3)

IDTypeTestExpected outcome
C-01PTLS 1.3 handshake against each of the four required endpointsSuccessful handshake with TLS 1.3 negotiated
C-02PValid certificate chain to a trusted CATrust path validates without warnings
C-03NConnect using TLS 1.2Connection rejected by the new MAP
C-04NConnect using a self-signed or expired certificateConnection refused; alert raised
C-05NAttempt cleartext HTTP on any CSF endpointConnection refused or auto-redirected to HTTPS
C-06ESNI requests for an unrelated host on the same IPServer returns a certificate matching the requested host or refuses, never the wrong cert
C-07ELong-lived idle connection across keepalive timeoutReconnect succeeds without leaking session state

#8.2 OAuth 2.0 (Client Credentials)

IDTypeTestExpected outcome
O-01PToken issued for a registered client with valid client_secretHTTP 200 with bounded-lifetime bearer token
O-02PToken used to call the Letterbox APIAPI accepts the token (subject to other validations)
O-03NInvalid client_secretHTTP 401, no token
O-04NExpired token used on Letterbox APIHTTP 401
O-05NToken issued by a different MAP's authorisation serverHTTP 401 (issuer/audience mismatch)
O-06NToken presented over HTTP (no TLS)Connection refused before token is parsed
O-07ETwo concurrent token refreshesNo race; both tokens valid, no token reuse beyond expiry
O-08EClock skew of ±60 seconds between MAPsToken still validates within the configured tolerance
O-09EClient secret rotation under loadOld secret stops working only after grace period; no production impact

#8.3 CP Registry & Directory API

IDTypeTestExpected outcome
D-01PPublish a registry containing one CP with processSupport.status=TESTRegistry validates against schema; peers can pull
D-02PUpdate map.version timestamp on every changePeers detect change and re-pull
D-03PPull peer registry and merge into Master RegistryMaster Registry contains the union; conflicts marked
D-04NPublish registry missing a mandatory field (e.g. map.contact)Peer fails schema validation; new MAP is notified
D-05NPublish a CP with malformed RCPID (not UUIDv4)Peer rejects the CP entry
D-06NPull registry with stale OAuth tokenHTTP 401; fall back to re-authentication
D-07ETwo CPs with identical brand names across different MAPsBoth appear in Directory; RCPID disambiguates
D-08ERegistry pulled at maximum recommended frequency (~60s) over 24hNo throttling; consistent freshness
D-09EA CP appears with status=TEST in one registry and status=ACTIVE in another (rare conflict)Receiving MAP excludes from its CP-facing Directory and raises an alert
D-10ERegistry contains a CP whose _mapkey DNS record points to a different MAPConflict-resolved per §5.5; DNS wins
D-11ETEST CPs MUST NOT appear in the CP-facing Directory shared with the new MAP's own CPsDirectory excludes all TEST and SUSPEND entries

#8.4 PKI / DKIM Signing

These are the highest-stakes tests. Every test below MUST be runnable against the NowYoYo PKI Test Harness before being repeated against the Sponsor MAP.

IDTypeTestExpected outcome
K-01PSign + verify round-trip with RSA-SHA256 (2048-bit)Verification passes
K-02PSign + verify round-trip with RSA-SHA256 (3072-bit)Verification passes
K-03PSign + verify round-trip with Ed25519-SHA256Verification passes
K-04PSign + verify round-trip with RSA-SHA512Verification passes
K-05PVerify against the public NowYoYo .txt endpointHTTP 200 with successful log
K-06PVerify against the public NowYoYo .json endpointHTTP 204
K-07NMutate one byte of the body after signingHTTP 403, error code 8101, body hash mismatch
K-08NMutate the X-CSF-SIGNATURE-DATESTAMP after signingHTTP 403, error code 8101, signature verification failure
K-09NSubmit RSA-1024 key (below minimum)Verifier rejects with 8101 (key too short)
K-10Nd= in signature does not match the directory entry for the CPHTTP 403, error code 8101 (domain not valid for CP)
K-11NMissing mandatory tag (b, bh, s, d, a, c, h)HTTP 403, error code 8101, listing missing tag(s)
K-12NDNS TXT record absent for the CP _domainkeyHTTP 403, error code 8102 (TEMP_FAIL)
K-13NDNS TXT record present but malformed (k=/p= invalid)HTTP 403, error code 8102
K-14NSignature uses SHA-1 (deprecated)HTTP 403, error code 8101
K-15NHeader casing wrong (x-csf-signature-datestamp lower-case in signature input)Verification fails — surfaces case-sensitivity bug
K-16EDKIM-Signature canonicalisation has trailing whitespaceVerifier still passes only if c=simple/simple is honoured exactly
K-17EBody containing only whitespace, then signedVerification passes — empty hash matches
K-18EReplay the same signed message after 10 minutesReceiver MAY accept or reject by configured window; behaviour MUST be documented
K-19EReplay the same signed message after 24 hoursReceiver MUST reject (replay window exceeded)
K-20ECP rotates DNS public key during onboardingOld signatures fail; new signatures pass within DNS TTL + handling buffer

#8.5 Letterbox API (Message Send/Receive)

IDTypeTestExpected outcome
L-01PPOST a valid signed JAM message to the new MAP's letterboxHTTP 202, no body
L-02PPOST to the new MAP for a CP it servesMessage delivered to that CP via the MAP's CP integration
L-03NPOST without Authorization headerHTTP 401
L-04NPOST with malformed JSON bodyHTTP 400
L-05NPOST with envelope missing source.identityHTTP 400, error code 9003
L-06NPOST to a destination RCPID not in the new MAP's registryHTTP 400, error code 9001
L-07NPOST with routingID not supported by destinationHTTP 400, error code 9012
L-08NDestination CP status=SUSPEND for the requested processHTTP 403, error code 9001
L-09NSource CP authorisation does not match originating MAPHTTP 401, error code 9004
L-10NSend a message that is unsigned (missing X-CSF-SIGNATURE)HTTP 403, error code 8101
L-11NSend a message via GET instead of POSTHTTP 405
L-12ESend a message of 100KB bodyHTTP 202; no truncation
L-13ESend a message of 1MB bodyHTTP 202 if within published limits, otherwise documented rejection
L-14ESend a message with auditData.test=true to a status=ACTIVE CPReceiver routes but excludes from production reporting
L-15EBurst of 100 messages in 10 secondsAll accepted, or rate-limited with HTTP 429 + Retry-After
L-16ESame correlationID used twice within the retry windowReceiver MUST treat as idempotent (no duplicate downstream delivery)

#8.6 SforB Industry Process Tests

This onboarding and testing process document is v1.0 of the TAG Baseline Test Suite — published by the TAG as a standalone supporting document for the CSF and for onboarding activities. The baseline is expected to iterate over time as additional tests are developed to support the Switching for Business protocol and as MAPs progress through onboarding and industry testing. The aim is to capture the lessons learnt during operation and to raise the quality bar of entry to the CSF network. The SforB industry-process tests below represent the agreed v1.0 baseline, which the TAG will evolve in subsequent revisions.

This baseline is also expected to grow continuously as the network goes live and operational experience surfaces additional cases (defects, near-misses, edge conditions, regulatory changes, GPLB-SG process refinements). Each new case agreed by the TAG MUST be added to this section and to the regression suite in Section 11, with the date of adoption recorded. See REVIEW.md §1.9 for the open action.

These tests verify the interaction between CSF transport and the SforB business process. The TAG's published test data assets and the Sponsor MAP's Virtual Test Host are used as counterparties.

IDTypeTestExpected outcome
S-01PSend businessSwitchMatchRequest for a known test CP with valid asset dataReceive businessSwitchMatchResponse with match=true
S-02PSend businessSwitchOrderRequest after a successful matchReceive businessSwitchOrderResponse with order accepted
S-03PReceive a businessSwitchMatchRequest and respond using cached customer data (without CP being online)Response within published SLA
S-04PCancel an in-flight order before completionCancellation acknowledged; downstream parties informed
S-05NSend businessSwitchMatchRequest against a CP with status=SUSPENDMatch request rejected with appropriate code
S-06NSend businessSwitchOrderRequest without a preceding matchOrder rejected with appropriate code
S-07NbusinessSwitchMatchRequest with corrupted asset dataMatch rejected with the correct SforB Response Code
S-08NOrder request with destination CP whose _mapkey DNS record points to a different MAPSender re-routes via DNS; receiver rejects misrouted attempt
S-09EMatch request crossing midnight (timestamp window edge)No false rejections due to date rollover
S-10EMatch request to a CP that has just transitioned MAP (within DNS TTL)Either old or new MAP responds; conflict resolved by DNS
S-11EConcurrent matches and orders on the same business assetSforB process tie-breaking rules apply (as defined by the GPLB-SG); no double-allocation
S-12ETest traffic (auditData.test=true) MUST NOT generate customer notifications, billing events, or KPI updatesVerified in receiving MAP's downstream reporting
S-13EA CP appears in registries for OTS and SforB with different statusesEach process evaluated independently
S-14ESwitch Match for a CP linked across OTS and SforB RCPIDs (R.26)Linkage resolved if implemented; degrades gracefully if not

#8.7 Retry & Delivery Policy

This test set exercises the CSF MAP-to-MAP delivery policy ratified in Part 2 §4.7. Tests R-01 to R-09 are behavioural (the original baseline) and validate response handling, error-code emission, and circuit-breaker activation. Tests R-10 to R-17 were added when §4.7 was ratified and validate the concrete numerical values now codified in the framework — connection / response timeouts, exponential-backoff shape, and circuit-breaker activation / recovery thresholds.

The HMAP boundary is out of scope of this test set — see the standalone TOTSCo HMAP Integration Guide §7 for HMAP-specific tests. The R-tests below all assume CSF MAP-to-MAP P2P traffic.

IDTypeTestExpected outcome
R-01PReceiving MAP returns HTTP 503 with Retry-After: 30Sender retries no sooner than 30s
R-02PReceiving MAP returns HTTP 429 with Retry-AfterSender backs off respecting header
R-03PTransient network failure mid-POSTSender retries with exponential backoff and jitter
R-04NReceiving MAP returns HTTP 400Sender does not retry; raises 9006 delivery failure
R-05NReceiving MAP returns HTTP 404Sender does not retry; raises 9007 delivery failure
R-06NSender exceeds maximum retry windowSender raises 9008 and stops
R-07ECircuit breaker opens after N consecutive failuresSubsequent sends fail fast and alert is raised
R-08ECircuit breaker closes after recovery periodNormal sending resumes
R-09ELong-lived 8102 (DNS) failureSender retries with backoff up to maximum window, then fails the message
R-10PConnection timeout — sender attempts delivery to a destination IP that drops the SYN. Measure the elapsed time before the sender treats the attempt as failed.Attempt fails at 1 second ± 200 ms. Sender records the failure, enters the retry schedule. Connection-time recorded in the audit log.
R-11PResponse timeout — destination accepts the TCP / TLS connection but never sends HTTP 202 (or any response). Measure the elapsed time before the sender abandons the attempt.Attempt fails at 3 seconds ± 500 ms after connection establishment. Sender records the failure, enters the retry schedule. Response-time recorded in the audit log.
R-12PExponential backoff shape — destination returns HTTP 5xx on every attempt for 5 minutes. Capture all retry intervals observed by the sender.Successive intervals show geometric growth (each interval ≥ 1.5× the previous) capped at a maximum (e.g. 60 s). Linear or fixed-cadence intervals MUST cause the test to fail.
R-13PJitter present — repeat R-12 with three independent senders against the same destination. Compare retry timestamps across senders.Retry timestamps do not align; observed pairwise variance ≥ ±15 % of the base interval. Aligned (no-jitter) timestamps MUST cause the test to fail.
R-14PCircuit-breaker activation — destination returns HTTP 5xx (or times out) on N consecutive attempts. Continue sending after the Nth failure.After N (recommended 5) consecutive failures the circuit opens; subsequent sends fail fast within 100 ms without attempting the destination; an operational alert is raised; the sender's outbound queue continues queueing new messages without further delivery attempts to that destination.
R-15PCircuit-breaker recovery — after the circuit has opened, wait the configured recovery period, then issue a single probe attempt to a destination that now responds normally.The probe attempt succeeds. The circuit transitions half-open → closed. Subsequent queued messages flow to the destination normally. A "circuit closed" event is recorded in the audit log.
R-16EHalf-open probe failure — after recovery period expires, probe attempt fails (destination still down).Circuit returns cleanly to the open state without flooding the destination with the queued backlog. Next recovery period begins. Alert state preserved (no false "recovered" notification).
R-17PBounded retry window — sender's configured maximum retry window expires while destination is still failing.Sender raises 9008 Delivery Failure for the originating message, increments the persistent-failure counter, and triggers the operational-escalation path: an alert is raised that names the destination RCPID, attempt count, and last error code. The sender MUST NOT continue retrying indefinitely.
R-18EOperational engagement via map/outage[] — destination MAP publishes a P1 outage entry covering the failure window.Sender's retry policy downgrades (see Part 2 §9.3 Outage Notification): retries pause or reduce in frequency until the outage entry clears. No additional sender-side alerts are raised while the outage is active.

#8.8 Conflict Resolution & DNS

IDTypeTestExpected outcome
F-01PCP appears in MAP A and MAP B registries during transitionSending MAP routes per _mapkey DNS record
F-02PCP rotates _mapkey to new MAPSubsequent messages route to new MAP after DNS TTL
F-03N_mapkey record absentSender raises 9005 (no valid route)
F-04N_mapkey points to an unknown MAPSender raises 9005
F-05ETwo _mapkey records present for the same RCPIDBehaviour MUST be defined: either reject (9005) or use deterministic ordering
F-06E_domainkey rotated while messages in flightIn-flight messages either complete or fail with 8102/8101; no silent corruption

#8.9 Security & Negative Edge Cases

IDTypeTestExpected outcome
Z-01NReplay a previously-accepted message verbatim within 10sReceiver MUST accept-or-reject per documented replay window; behaviour MUST be deterministic
Z-02NReplay a previously-accepted message after 24hReceiver MUST reject
Z-03NSend from a MAP whose OAuth credentials have been revokedHTTP 401
Z-04NSubmit a message claiming a source.identity whose _domainkey is hosted on an unrelated domainHTTP 403, error code 8101
Z-05NSubmit a message with a d= value that resolves to a wildcard DNS entryVerifier rejects (CP-specific selector required)
Z-06NSend messages from an IP not associated with the registered MAPAcceptable per CSF (DKIM is the trust mechanism), but logging MUST capture the discrepancy
Z-07EDoS: 10× expected peak loadMAP either serves successfully or returns HTTP 429 with Retry-After; no silent drops
Z-08EBody containing characters at byte boundaries (UTF-8 multibyte split)Body hash unaffected; verification passes
Z-09ETLS session resumption across restartsNew session established cleanly; no validation bypass

#8.10 Operational Validation

IDTypeTestExpected outcome
V-01P72-hour soak at projected production loadAll SLAs met; no resource leaks
V-02PService Status endpoint returns availableOther MAPs read and act on it
V-03PAudit log contains every send/receive with correlation IDsLogs retrievable for the CSF retention period
V-04EPlanned maintenance windowService Status indicates planned; peers handle gracefully
V-05EUnplanned outage: endpoint down for 5 minutesPeers retry; message delivery resumes; nothing lost beyond documented retry window

#9. Exit Criteria

A new MAP is granted production status only when all of the following hold:

  1. All baseline tests in §8.1 to §8.9 pass with evidence captured by the Sponsor MAP.
  2. Section 8.10 operational validation has run for the full 72-hour soak window.
  3. The TAG hidden test set (a confidential rotating subset known only to the TAG) has been executed by the Sponsor MAP and passed.
  4. Bilateral connectivity has been demonstrated with at least 50% of currently onboarded MAPs (rising over time as the network grows).
  5. PKI verification has been demonstrated against the public NowYoYo harness for both RSA and Ed25519 algorithms.
  6. The new MAP has published a complete CP Registry with no TEST entries that should not be there.
  7. The new MAP commits to the Memorandum of Understanding (MoU).
  8. TAG steering group approval is recorded at a weekly meeting.

Note: The hidden test set evolves continuously. New cases are added when an incident reveals a behavioural gap. This is intentional — onboarding rigour grows with the network's experience.

MAP-of-1 variant. For single-cp engagements, the criteria above are amended per Part 1 §7.4.9: criterion 2 (72-hour soak) is waived, and criterion 4 (≥50% bilateral coverage) is replaced with "bilateral with all MAPs and HMAPs the candidate intends to exchange with at go-live, including TOTSCo as HMAP if applicable." Criteria 1, 3, 5, 6, 7, 8 apply unchanged. The waivers are recorded in the Phase 5 approval record.

#10. Bilateral Testing

When two MAPs need to establish a new direct relationship outside Sponsor MAP onboarding (for example, a previously-onboarded MAP adding support for a new feature or routing group), the bilateral test sequence is the relevant subset of §8:

StageTests
Connectivity§8.1, §8.2
Registry interchange§8.3
PKI§8.4 (full suite, against NowYoYo harness then against the peer)
Letterbox§8.5 (P-tests minimum)
Industry process§8.6 (subset relevant to the new relationship)

Bilateral tests do not require Sponsor MAP involvement, but the result MUST be reported to the TAG so the broader directory can be kept consistent.

#11. Regression Suite for Ongoing Compliance

The TAG publishes a regression suite that all onboarded MAPs are expected to run on:

  • Every CSF version increment (e.g. transport spec change, new error codes, schema additions)
  • Every internal release that touches the CSF transport, signing, or directory pipeline
  • Quarterly, as a baseline conformance check

Failure of any regression test MUST be reported to the TAG within one working day. The TAG may, in consultation with the steering group, mark a MAP's map.status as SUSPEND if regression failures threaten network integrity.

#12. Tooling Reference

Tool / ResourceProviderPurpose
NowYoYo PKI Test Harness (sec-key-test.csf.nowyoyo.net)NowYoYo (free, public)Round-trip DKIM signing/verification with detailed logs
CSF Verification Testing Endpoint WikiNowYoYoCompanion documentation for the test harness
Sponsor MAP Virtual Test HostEach Sponsor MAPEnd-to-end SforB process simulation
Java DKIM referenceApache James jDKIMLibrary implementation of RFC 6376
.NET DKIM referenceMailKitLibrary implementation of RFC 6376
OpenAPI specificationTAG (letterbox-openapi.yaml)Letterbox contract for tooling and mock generation

#13. References

#14. Document Control

VersionDateDescription
1.0 (Draft)2026-04-28Initial publication. Replaces the placeholder reference in REVIEW.md §1.8. PKI test harness imported from public Gist.