8. Palm Verification¶
Palm enrollment and verification — the PalmVerifier port, score model, duplicate detection, palm-type locking, and the small/large model split.
8.1 Overview¶
Identity Platform uses the same ports and adapters (hexagonal) architecture for palm biometric verification as it does for KYC. The core domain defines a standard PalmVerifier interface, and vendor-specific adapters implement it. This allows the platform to swap or run multiple palm verification vendors without changing business logic.
8.2 Architecture¶
┌─────────────────────────────────────────────────────┐
│ Identity Platform │
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Core Domain │ │
│ │ │ │
│ │ User, PalmTemplate, Enrollment, Challenge │ │
│ │ │ │
│ └─────────────────────────────────────────────┘ │
│ │ │
│ PalmVerifier Port │
│ (interface) │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ X-Telcom BioWave Pass │ │
│ │ Adapter (sole MVP) │ │
│ └───────────────────────┘ │
│ │
└─────────────────────────────────────────────────────┘
8.3 PalmVerifier Port (Interface)¶
The PalmVerifier Port defines the abstract interface that all palm verification vendor adapters must implement.
| Method | Parameters | Returns | Description |
|---|---|---|---|
enroll |
user_id, palm_template |
EnrollmentResult |
Store a palm template for a user |
verify |
user_id, palm_scan |
VerificationResult |
1:1 match against stored template |
identify |
palm_scan, tenant_id |
IdentificationResult |
1:N search across all templates |
get_status |
user_id |
EnrollmentStatus |
Check if user has an enrolled template |
delete_template |
user_id |
boolean |
Remove a stored palm template |
verify (1:1) vs identify (1:N) — the difference is whether the caller already names the user:
verify(1:1) checks a claimed identity: the caller already knows theuser_id(e.g. an e-signature challenge carries it), and the platform compares against that one template. This is the public, challenge-based path for personal scanners (§9.2, §13).identify(1:N) answers "who is this?" from a bare palm with no claim. Device-initiated transactions are claim-less, so they always useidentify— and it runs internally inside the broker (§10), not as a public endpoint.
8.4 Score Model¶
X-Telcom BioWave Pass returns a 4-element scores array alongside a 4-element thresholds array per query — IR and RGB features evaluated against the large and small models. Each index corresponds to a model variant:
| Index | Variant |
|---|---|
| 0 | Large-model IR |
| 1 | Large-model RGB |
| 2 | Small-model IR |
| 3 | Small-model RGB |
Match Policy (configured per tenant via palm_match_policy):
| Policy | Behavior |
|---|---|
all_thresholds |
All 4 scores must be ≥ corresponding thresholds (default — most strict) |
majority |
At least 3 of 4 scores ≥ corresponding thresholds (relaxed) |
any |
At least 1 score ≥ corresponding threshold (least strict — discouraged) |
Under the large model, the platform applies the policy after receiving the vendor response — the matching decision (matched: true/false) is platform-controlled, not vendor-controlled. Under the small model the SDK/verification server decides and the platform records the device-reported result (§8.13).
8.5 Verification Result¶
{
"user_id": "user_123",
"matched": true,
"scores": [1.0, 1.0, 1.0, 0.9495],
"thresholds": [0.7018, 0.7211, 0.7072, 0.7253],
"match_policy": "all_thresholds",
"latency_ms": 180,
"vendor": "biowave",
"timestamp": "2026-02-25T10:00:00Z"
}
8.6 Identification Result¶
{
"matched": true,
"user_id": "user_123",
"palm_id": 100001,
"scores": [1.0, 1.0, 1.0, 0.9495],
"thresholds": [0.7018, 0.7211, 0.7072, 0.7253],
"match_policy": "all_thresholds",
"candidates": 1,
"search_pool_size": 124500,
"latency_ms": 850,
"vendor": "biowave",
"timestamp": "2026-02-25T10:00:00Z"
}
8.7 Supported Palm Vendors¶
| Vendor | Technology | Strengths | Integration |
|---|---|---|---|
| X-Telcom BioWave Pass | Palm vein (IR + RGB; large/small models) | Sole MVP vendor; large-scale 1:N comparison; self-hostable | REST API (/KZ/*) |
Additional vendors can be plugged in behind the PalmVerifier port (§8.3) with no business-logic change; BioWave Pass is the only vendor documented for MVP.
8.8 Vendor Configuration (per Tenant)¶
{
"tenant_id": "wallet",
"palm_config": {
"vendor": "biowave",
"vendor_config": {
"base_url": "http://palm.internal.link.sa:8080",
"request_id_header": "request_id",
"timeout_ms": 2000
},
"match_policy": "all_thresholds",
"duplicate_check_enabled": true,
"duplicate_action": "reject"
}
}
8.9 Palm Verification Flow¶
Device-initiated (pos/gate — 1:N identify, large model):
1. User scans palm at the device
2. Device → Identity: POST /v1/device/transactions (mTLS) with {scan, context}
3. Identity → PalmVerifier Port → Vendor Adapter → Vendor API (1:N identify)
4. Vendor returns 4-element scores + thresholds per candidate
5. Identity applies the tenant's match_policy (§8.4) to decide the match
6. Match → Identity calls the bound product's authorize endpoint, relays the verdict (§10)
7. Identity returns {decision, display_message, product_reference} to the device in-band
Personal scanners (1:1 verify / e-signature) use the challenge-poll flow instead — see §9.2.
Under the small model this server-side identify step is replaced by the device SDK's direct match + report — see §8.13.
8.10 Pre-Enrollment Duplicate Detection¶
Detects identity fraud and accidental re-enrollment before committing a new palm template. Opt-in per tenant via palm_duplicate_check_enabled.
Flow:
1. Before committing the enrollment, the platform runs a 1:N POST /KZ/query with the new palm features.
2. BioWave returns the best match (user_id, id, 4-element scores/thresholds). A true duplicate also surfaces vendor code 30007 (feature already registered) on /KZ/add.
3. Platform applies the tenant's palm_match_policy: a conflict exists when a match passes thresholds AND has a different user_id than the enrolling user.
4. If a conflict is found, the configured palm_duplicate_action determines what happens:
| Action | Behavior |
|---|---|
reject |
Enrollment fails with HTTP 409. User cannot enroll until ops reviews. Audit event: palm_duplicate_detected with severity: high and matched_user_ids. |
flag |
Enrollment proceeds but a review_case record is created for ops to investigate. |
When to enable: High-value tenants (banks, regulated finance) and any tenant where a single user enrolling under multiple identities is a fraud risk.
Performance impact: Adds one extra round-trip to the vendor per enrollment (~200–800ms). Not enabled by default.
Diagram 4.1 — Pre-Enrollment Duplicate Detection
sequenceDiagram
autonumber
participant Kiosk as POS / Kiosk
participant Wallet as Wallet Backend
participant Identity as Identity Platform
participant Palm as X-Telcom BioWave Pass Palm Server
participant DB as PostgreSQL
participant Audit as Audit Log
Note over Kiosk: User has placed palm,<br/>features extracted
Kiosk->>Wallet: POST /enroll/palm<br/>{user_id, features, images, type}
Wallet->>Identity: POST /v1/enroll<br/>{user_id, palm_template, type: "left"}
Identity->>DB: Get tenant config
DB-->>Identity: {palm_duplicate_check_enabled: true,<br/>duplicate_action: "reject" | "flag"}
rect rgb(255, 245, 230)
Note over Identity, Palm: Step 1: Similarity search<br/>(top-5 candidates)
Identity->>Palm: POST /KZ/query<br/>Headers: request_id: <uuid><br/>Content-Type: multipart/form-data<br/>Body:<br/>- features_rgb.bin (optional)<br/>- features_ir.bin (optional)<br/>- image_rgb.png (required)<br/>- image_ir.png (required)<br/>- metadata: {type: "left",<br/>is_encrypted: false}<br/>(returns up to 5 candidates,<br/>deduped by user_id)
alt Candidates above threshold returned
Palm-->>Identity: {code: 0,<br/>data: {results: [up to 5 candidates,<br/>each with user_id, id, scores: [4]],<br/>thresholds: [4]}}
Identity->>Identity: Filter results where<br/>all 4 scores ≥ thresholds<br/>AND user_id != current user_id
alt Match found — possible fraud/duplicate
Identity->>Audit: Log: palm_duplicate_detected<br/>(enrolling_user_id, matched_user_ids,<br/>scores, severity: high)
alt Tenant config: reject
Identity-->>Wallet: 409 Conflict<br/>{error: "duplicate_palm",<br/>message: "Biometrics already registered"}
Wallet-->>Kiosk: "Cannot enroll —<br/>contact support"
else Tenant config: flag for review
Identity->>DB: Create review_case<br/>(enrolling_user_id, matched_user_ids,<br/>status: pending)
Note over Identity: Continue to /KZ/add<br/>but flag for ops review
end
else No conflicting match
Note over Identity: Safe to enroll —<br/>proceed to the /KZ/add enrollment step (Section 4.4)
end
else No candidate match (platform-decided)
Palm-->>Identity: {code: 0}<br/>(no candidate ≥ thresholds → platform no-match)
Note over Identity: Clean palm, proceed to enroll
end
end
rect rgb(230, 255, 230)
Note over Identity, Palm: Step 2: Proceed with enrollment
Note over Identity: See Section 5.3 for the full /KZ/add<br/>request/response; error codes 30007/30005<br/>in PRD §15
Identity->>Palm: POST /KZ/add (see Section 4.4)
Palm-->>Identity: {code: 0, data: {user_id, id, query_type}}
Identity->>DB: Mark palm_enrolled = true,<br/>store palm_id
Identity->>Audit: Log: palm_enrolled
Identity-->>Wallet: {status: "enrolled"}
end
8.11 Palm Type Restrictions¶
Per-user restriction on which palm types can verify that user. Useful for high-security tenants and account-suspension scenarios.
Endpoint: PUT /v1/users/{user_id}/palm-restriction (Console session, Tenant Admin or Operator)
Body:
Allowed values for query_type:
| Value | Behavior |
|---|---|
left |
Only left-palm verifications allowed |
right |
Only right-palm verifications allowed |
all |
Both palms allowed (default after enrollment) |
disable |
All palm verifications blocked without deleting the template (used for soft-suspension) |
When a verification or identification call is made and the user's restriction does not allow the submitted palm type, the platform returns 403 Forbidden with error palm_type_restricted (mapped from vendor error code 30008 — see §15).
Audit event: palm_restriction_set (records user_id, query_type, and set_by).
Common patterns:
- After enrollment, automatically lock to the enrolled palm type (e.g., left)
- On account suspension, set to disable
- On reinstatement, restore to original
For the sequence diagram, see Section 4.2 of identity-platform-diagrams.md.
Diagram 4.2 — Lock User to Specific Palm Type
sequenceDiagram
autonumber
participant Admin as Tenant Admin
participant Console as Web Console
participant Identity as Identity Platform
participant Palm as X-Telcom BioWave Pass Palm Server
participant DB as PostgreSQL
participant Audit as Audit Log
Note over Admin, Audit: Use cases:<br/>- Lock to specific palm after enrollment<br/>- Suspend palm verification on account hold<br/>- Re-enable on reinstatement
Admin->>Console: Set palm restriction for user<br/>(e.g., "left only" or "disable")
Console->>Identity: PUT /v1/users/{user_id}/palm-restriction<br/>Authorization: Bearer <token><br/>{query_type: "left"}
Identity->>DB: Validate user belongs to tenant
DB-->>Identity: User found
Identity->>Palm: POST /KZ/set_query_type<br/>Headers: request_id: <uuid><br/>Content-Type: application/json<br/>Body: {user_id, query_type: "left"}
alt Success — code: 0
Palm-->>Identity: {code: 0, msg: "Success"}
Identity->>DB: Update user.palm_query_type = "left"
Identity->>Audit: Log: palm_restriction_set<br/>(user_id, query_type: "left",<br/>set_by: admin_id)
opt Webhook subscribed
Identity->>Identity: Send webhook: user.palm_restriction_changed<br/>{user_id, query_type}
end
Identity-->>Console: {user_id, query_type: "left",<br/>updated_at}
Console-->>Admin: "Restriction applied"
else User not found in X-Telcom BioWave Pass — code: 30006
Palm-->>Identity: {code: 30006,<br/>msg: "DB ID not found"}
Identity->>Audit: Log: palm_restriction_failed<br/>(user_id, reason: not_enrolled)
Identity-->>Console: 404 Not Found<br/>"User not enrolled in palm"
end
Note over Admin, Audit: Subsequent /v1/device/transactions or /v1/verify<br/>calls with mismatched palm_type<br/>will fail with code 30008<br/>(see Section 4.3 — query type forbidden)
8.12 Operational¶
8.12.1 Health Monitoring¶
The platform monitors vendor reachability (POST /KZ/connect) and version (GET /pv/version) on a periodic cadence (default: every 60s). Failed checks trigger a palm_vendor_unhealthy audit event and page the on-call rotation. Version changes trigger palm_vendor_version_change for traceability.
For the sequence diagram, see Section 9.1 of identity-platform-diagrams.md.
Diagram 9.1 — X-Telcom BioWave Pass Health Check
sequenceDiagram
autonumber
participant Cron as Health Monitor
participant Identity as Identity Platform
participant Palm as X-Telcom BioWave Pass Palm Server
participant Audit as Audit Log
participant Alert as Ops Alerting
Cron->>Identity: Trigger health check<br/>(startup / interval / post-deploy)
par Connection test
Identity->>Palm: POST /KZ/connect<br/>Headers: request_id: <uuid><br/>Content-Type: application/json<br/>Body: (none)
alt Reachable
Palm-->>Identity: {code: 0, msg: "Success"}
else Unreachable
Palm-->>Identity: timeout / network error
end
and Version probe
Identity->>Palm: GET /pv/version<br/>Headers: request_id: <uuid><br/>(no body, response is text/plain)
alt Reachable
Palm-->>Identity: "0.0.13" (text/plain)
else Unreachable
Palm-->>Identity: timeout / 5xx
end
end
alt All checks passed
Identity->>Identity: Update health status: healthy
opt Version changed since last check
Identity->>Audit: Log: palm_vendor_version_change<br/>(old: "0.0.12", new: "0.0.13")
end
else Any check failed
Identity->>Identity: Update health status: unhealthy
Identity->>Audit: Log: palm_vendor_unhealthy<br/>(reason, severity: high)
Identity->>Alert: Page on-call —<br/>X-Telcom BioWave Pass unreachable
end
8.12.2 Threshold Configuration¶
Platform Admins can tune the vendor's 4 global thresholds (vendor POST /KZ/set_thresholds) via GET/PUT /v1/admin/palm/thresholds. Used for:
- Tightening after a fraud incident
- Per-environment calibration (staging vs. production)
- Trade-off tuning (false-accept vs. false-reject rate)
Thresholds are global at the vendor side (not per-tenant). Tenant-level relaxation is done through palm_match_policy instead.
Audit event: palm_thresholds_updated (records old and new threshold values).
For the sequence diagram, see Section 9.2 of identity-platform-diagrams.md.
Diagram 9.2 — X-Telcom BioWave Pass Threshold Configuration
sequenceDiagram
autonumber
participant Admin as Platform Admin
participant Console as Web Console
participant Identity as Identity Platform
participant Palm as X-Telcom BioWave Pass Palm Server
participant DB as PostgreSQL
participant Audit as Audit Log
Note over Admin: Use cases:<br/>- Tighten thresholds after fraud event<br/>- Loosen for lower-security environment<br/>- Tune per-model variant accuracy
Admin->>Console: Open palm threshold settings
Console->>Identity: GET /v1/admin/palm/thresholds<br/>Authorization: Bearer <admin-token>
Identity->>DB: Get current thresholds (cached)
DB-->>Identity: thresholds: [4 floats]
Identity-->>Console: {thresholds: [4],<br/>last_updated_at, updated_by}
Console-->>Admin: Display current thresholds<br/>(annotated with model variant per index)
Admin->>Console: Update thresholds<br/>(e.g., bump index 0 from 0.70 → 0.75)
Console->>Identity: PUT /v1/admin/palm/thresholds<br/>Authorization: Bearer <admin-token><br/>{thresholds: [4 floats]}
Identity->>Identity: Validate: all values 0.0–1.0,<br/>array length == 4
Identity->>Palm: POST /KZ/set_thresholds<br/>Headers: request_id: <uuid><br/>Content-Type: application/json<br/>Body: {thresholds: [4 floats]}
alt Success
Palm-->>Identity: {code: 0, msg: "Success"}
Identity->>DB: Persist thresholds<br/>(audit trail)
Identity->>Audit: Log: palm_thresholds_updated<br/>(updated_by: admin_id,<br/>old_thresholds, new_thresholds)
Identity-->>Console: {status: "updated",<br/>thresholds, updated_at}
Console-->>Admin: "Thresholds updated"
else Validation/permission error
Palm-->>Identity: {code: 10001, msg: "..."}
Identity-->>Console: 400 Bad Request
Console-->>Admin: Error message
end
8.13 Palm Model (Small vs Large)¶
The palm match runs in one of two models, chosen once per deployment by a Platform Admin — deployment-wide, not per-tenant or per-user (like the global thresholds in §8.12.2). The two models differ in one thing: who runs the match.
- Large model — the platform matches. The device sends the scan to the platform, which calls the verification server over
/KZ/*, appliespalm_match_policy(§8.4), and owns the decision. This is the model the rest of §8 describes. - Small model — the device matches. The device's client SDK calls the verification server directly; the platform sits outside the match path. The device reports the result — matched
user_id+ a pass/fail boolean — to the platform over its mTLS channel (§9.2). The platform records it and, forpos/gate, then authorizes the bound product with thatuser_id(§10).
Why it's a different topology, not just another vendor. Because the platform matches only in the large model, the PalmVerifier port (§8.3) abstracts the large model only — the small model is not another adapter behind that port. Under the small model the platform has no verification-server connection at all; one is added only to migrate (§8.14).
What each model supports:
| Feature | Large model | Small model |
|---|---|---|
palm_match_policy / 4-element score model (§8.4) |
platform decides | N/A — SDK/server decides; platform records boolean |
| Pre-enrollment duplicate detection (§8.10) | available | N/A |
| Global threshold config (§8.12.2) | applies | N/A (SDK/server-side) |
| Palm type restrictions (§8.11) | applies | N/A in matching path |
PalmVerifier-port identify/verify (§8.3) |
yes | no — device SDK direct |
| Platform role | matcher + router | recorder + router (device-trusted) |
Same endpoints, different payload. The active model decides what every device-facing palm endpoint carries: under the large model the device sends scans (the platform matches); under the small model it sends results (the platform records). This covers /v1/device/transactions, /v1/device/enroll, and the challenge endpoints /v1/verify + /v1/enroll + /v1/challenges/{id}/complete (transaction, enrollment, and verify flows). The platform implements both paths; migration (§8.14) flips which one is live.
Trust trade-off. Small-model results are device-trusted — the platform makes no independent match decision (mTLS authenticates the reporting device, not the match itself). That fits pilot / early-stage scale; the device-trust posture and capacity are the two reasons to move to the large model for production or fraud-sensitive scale.
This section is the canonical definition of the palm model (referenced by §5.7, §10.3, §19.1). Sequence diagram: identity-platform-diagrams.md §4.5.
Diagram 4.5 — Small-Model Device Transaction (Client SDK Direct)
sequenceDiagram
autonumber
participant User
participant Device as POS / Gate (client SDK)
participant Palm as X-Telcom BioWave Pass (verification server)
participant Identity as Identity Platform
participant Product as Wallet / Access Backend (linked service)
participant Audit as Audit Log
User->>Device: Place palm on scanner
Device->>Device: Capture palm image (RGB + IR)
rect rgb(255, 245, 230)
Note over Device, Palm: Device-trusted — match decided SDK/server-side,<br/>NOT by the platform
Device->>Palm: SDK 1:N match (direct — no platform on path)
Palm-->>Device: {user_id, pass/fail, scores}
end
alt Match (SDK pass)
Device->>Identity: POST /v1/device/transactions<br/>(mTLS — device cert)<br/>{user_id, matched: true,<br/>context, idempotency_key}
Identity->>Identity: Parse SAN URI → device_id, tenant_id;<br/>record result (no /KZ/* call);<br/>skip server-side identify;<br/>sign identity_assertion JWT
Identity->>Product: POST {base_url}{authorize_path}<br/>Authorization: Bearer <identity_assertion><br/>{user_id, action, context, idempotency_key}
Product-->>Identity: {decision, display_message,<br/>reference_id, ttl}
Identity->>Audit: Log: device_transaction<br/>(source: device_reported, decision)
Identity-->>Device: {decision, display_message,<br/>product_reference} (in-band)
Device-->>User: Open gate / "Approved" (or decline)
else No match (SDK fail)
Device->>Identity: POST /v1/device/transactions<br/>{matched: false, context, idempotency_key}
Identity->>Audit: Log: device_transaction (not_recognized)
Identity-->>Device: {decision: "not_recognized"}
Device-->>User: "Palm not recognized"
end
Diagram 4.6 — Small-Model Device Enrollment (Client SDK Direct)
sequenceDiagram
autonumber
participant User
participant Device as POS / Kiosk (client SDK)
participant Identity as Identity Platform
participant Palm as X-Telcom BioWave Pass (verification server)
participant SMS as SMS Provider
participant Audit as Audit Log
User->>Device: Tap "Enroll" / "Sign up"
Device->>Identity: POST /v1/device/signup (mTLS)<br/>{mobile}
Identity->>Identity: Find or create user by mobile<br/>(tenant-scoped)
Identity->>SMS: Send OTP
SMS-->>User: OTP code
User->>Device: Enter OTP
Device->>Identity: POST /v1/device/signup/verify (mTLS)<br/>{challenge_id, code}
Identity-->>Device: {user_id, is_new_user,<br/>palm_enrolled: false,<br/>consent_status, kyc_status}
opt Product requires KYC and kyc_status != verified
Device-->>User: Direct to complete KYC first<br/>(app / Nafath)
end
opt consent_required and consent_status = none
Device->>User: Show consent screen
User->>Device: Accept
Device->>Identity: POST /v1/device/consent (mTLS)<br/>{user_id, consent_type, version}
Identity->>Audit: Log: consent.granted
end
Note over Device: Consent is gated HERE, device-side, BEFORE enrolling —<br/>the platform is off the enroll path under the small model.
User->>Device: Place palm
Device->>Device: Capture palm image (RGB + IR)
rect rgb(255, 245, 230)
Note over Device, Palm: Device-trusted — enrollment done SDK/server-side,<br/>NOT by the platform
Device->>Palm: SDK enroll (direct — no platform on path)
Palm-->>Device: {palm_id, success}
end
Device->>Identity: POST /v1/device/enroll (mTLS)<br/>{user_id, enrolled: true}
Identity->>Identity: Record palm_enrolled (no /KZ/add);<br/>trust device-reported result
Identity->>Audit: Log: enrollment.complete<br/>(source: device_reported)
opt Webhook subscribed
Identity->>Identity: Send webhook: enrollment.complete
end
Identity-->>Device: {palm_enrolled: true}
Device-->>User: "Enrolled — palm now works across Link"
8.14 Small→Large Migration¶
A Platform Admin migrates a deployment from the small model to the large model. The migration is one-way and reprocesses the already-stored RGB+IR enrollments into large-model representations — no re-enrollment and no user action.
Prerequisite — verification-server endpoint. Under the small model the platform has no connection to the verification server (the device SDK does). Before migrating — and for large-model operation afterward — a Platform Admin configures the deployment's verification-server endpoint (connection URL) so the platform can reach it (GET/PUT /v1/admin/palm/verification-server, §14.2; stored deployment-level, §5.7).
- The Admin triggers migration in the Console (
POST /v1/admin/palm/migrate, §14.2). Emitspalm_model_migration_started. - The platform connects to the configured verification-server endpoint and runs the vendor-supplied migration script against it; the server reprocesses the stored RGB+IR enrollments into large-model representations.
- The platform monitors progress (
GET /v1/admin/palm/modelsurfacesmigration_status). - On success the platform switches the deployment's palm model small→large. This switch is the platform-side cutover: request handling atomically moves from the small-model record path to the large-model
/KZ/*broker path for the whole deployment (in-flight small-model operations complete under the old model). Palm flows then follow the large-model server-API path (§8.9, §10). Emitspalm_model_migration_completedandpalm_model_changed; failure emitspalm_model_migration_failedand leaves the model unchanged.
The migration script is vendor-supplied — the platform runs it against the configured verification-server endpoint; it does not author the reprocessing logic.
For the sequence diagram, see Section 9.3 of identity-platform-diagrams.md.
Diagram 9.3 — Small→Large Model Migration
sequenceDiagram
autonumber
participant Admin as Platform Admin
participant Console as Web Console
participant Identity as Identity Platform
participant Palm as X-Telcom BioWave Pass (verification server)
participant DB as PostgreSQL
participant Audit as Audit Log
Note over Admin, Identity: Prerequisite — verification-server endpoint configured<br/>(PUT /v1/admin/palm/verification-server — §13.2)
Admin->>Console: "Migrate to large model" (confirm)
Console->>Identity: POST /v1/admin/palm/migrate
Identity->>Audit: Log: palm_model_migration_started
rect rgb(255, 245, 230)
Note over Identity, Palm: Platform runs the vendor-supplied migration script<br/>against the configured verification-server endpoint
Identity->>Palm: Connect + run migration script<br/>(reprocess stored RGB+IR → large model)
loop Until complete
Identity->>Palm: Poll migration progress
Palm-->>Identity: {status}
end
end
alt Migration succeeded
rect rgb(230, 255, 230)
Identity->>DB: Switch deployment palm model small→large
Identity->>Audit: Log: palm_model_migration_completed + palm_model_changed
Identity-->>Console: {model: "large", migration_status: "completed"}
Console-->>Admin: "Migration complete — flows now use the large-model server API (§4.3)"
end
else Migration failed
Identity->>Audit: Log: palm_model_migration_failed (model unchanged)
Identity-->>Console: 500 {error: migration_failed}
Console-->>Admin: Error — deployment stays on the small model
end