From a3a53992bd341a1bf93742a55c3fc04ed0c4bfed Mon Sep 17 00:00:00 2001 From: Joshua Date: Thu, 19 Feb 2026 18:13:05 -0800 Subject: [PATCH] W0: add regulatory profile baseline to checkout and ID flows --- backend/secretapi/README.md | 1 + backend/secretapi/app.go | 37 +++++++------- backend/secretapi/app_test.go | 11 +++++ backend/secretapi/config.go | 5 ++ backend/secretapi/config_test.go | 18 +++++++ backend/secretapi/marketplace.go | 55 +++++++++++---------- backend/secretapi/marketplace_models.go | 28 ++++++----- backend/secretapi/models.go | 35 +++++++------ backend/secretapi/regulatory_profile.go | 30 +++++++++++ docs/api/examples/marketplace.examples.md | 2 + docs/api/examples/secret-system.examples.md | 3 ++ docs/api/marketplace.openapi.yaml | 10 +++- docs/api/secret-system.openapi.yaml | 15 ++++-- docs/secret-system-spec.md | 4 ++ 14 files changed, 177 insertions(+), 77 deletions(-) create mode 100644 backend/secretapi/regulatory_profile.go diff --git a/backend/secretapi/README.md b/backend/secretapi/README.md index 47794b7..19a4ada 100644 --- a/backend/secretapi/README.md +++ b/backend/secretapi/README.md @@ -153,6 +153,7 @@ The envelope is pre-execution pricing metadata and is authoritative for checkout - `SECRET_API_QUOTE_TTL_SECONDS` (default `900`) - `SECRET_API_WALLET_SESSION_TTL_SECONDS` (default `2592000`) - `SECRET_API_REQUIRE_WALLET_SESSION` (default `true`; set `false` only for controlled local harness/debug usage) +- `SECRET_API_REGULATORY_PROFILE_ID` (`us_general_2026` default, `eu_ai_act_2026_baseline` supported) - `SECRET_API_DOMAIN_NAME` - `SECRET_API_VERIFYING_CONTRACT` - `SECRET_API_MEMBERSHIP_CONTRACT` diff --git a/backend/secretapi/app.go b/backend/secretapi/app.go index 0dbb56f..8eb6962 100644 --- a/backend/secretapi/app.go +++ b/backend/secretapi/app.go @@ -465,22 +465,23 @@ func (a *app) handleMembershipQuote(w http.ResponseWriter, r *http.Request) { "value": quote.ValueHex, } writeJSON(w, http.StatusOK, membershipQuoteResponse{ - QuoteID: quote.QuoteID, - ChainID: quote.ChainID, - Currency: quote.Currency, - AmountAtomic: quote.AmountAtomic, - Decimals: quote.Decimals, - CostEnvelope: newQuoteCostEnvelope(quote.Currency, quote.Decimals, quote.AmountAtomic), - Deadline: quote.ExpiresAt.Format(time.RFC3339Nano), - ContractAddress: quote.ContractAddress, - Method: quote.Method, - Calldata: quote.Calldata, - Value: quote.ValueHex, - OwnerWallet: address, - PayerWallet: payerAddress, - SponsorshipMode: sponsorshipMode, - SponsorOrgRoot: strings.TrimSpace(req.SponsorOrgRoot), - Tx: tx, + QuoteID: quote.QuoteID, + ChainID: quote.ChainID, + RegulatoryProfileID: a.cfg.RegulatoryProfileID, + Currency: quote.Currency, + AmountAtomic: quote.AmountAtomic, + Decimals: quote.Decimals, + CostEnvelope: newQuoteCostEnvelope(quote.Currency, quote.Decimals, quote.AmountAtomic), + Deadline: quote.ExpiresAt.Format(time.RFC3339Nano), + ContractAddress: quote.ContractAddress, + Method: quote.Method, + Calldata: quote.Calldata, + Value: quote.ValueHex, + OwnerWallet: address, + PayerWallet: payerAddress, + SponsorshipMode: sponsorshipMode, + SponsorOrgRoot: strings.TrimSpace(req.SponsorOrgRoot), + Tx: tx, }) } @@ -602,6 +603,7 @@ func (a *app) handleMembershipConfirm(w http.ResponseWriter, r *http.Request) { Status: "membership_active", DesignationCode: rec.Code, DisplayToken: rec.DisplayToken, + RegulatoryProfileID: a.cfg.RegulatoryProfileID, TxHash: strings.ToLower(req.TxHash), ActivatedAt: now.Format(time.RFC3339Nano), IdentityAssurance: rec.IdentityAssurance, @@ -639,7 +641,7 @@ func (a *app) handleMembershipStatus(w http.ResponseWriter, r *http.Request) { if err != nil { if errors.Is(err, errNotFound) { - writeJSON(w, http.StatusOK, membershipStatusResponse{Status: "none"}) + writeJSON(w, http.StatusOK, membershipStatusResponse{Status: "none", RegulatoryProfileID: a.cfg.RegulatoryProfileID}) return } writeError(w, http.StatusInternalServerError, "failed to resolve membership status") @@ -654,6 +656,7 @@ func (a *app) handleMembershipStatus(w http.ResponseWriter, r *http.Request) { Status: status, Wallet: rec.Address, DesignationCode: rec.Code, + RegulatoryProfileID: a.cfg.RegulatoryProfileID, IdentityAssurance: normalizeAssuranceLevel(rec.IdentityAssurance), IdentityAttestedBy: strings.TrimSpace(rec.IdentityAttestedBy), IdentityAttestationID: strings.TrimSpace(rec.IdentityAttestationID), diff --git a/backend/secretapi/app_test.go b/backend/secretapi/app_test.go index 19b1204..6fa2c31 100644 --- a/backend/secretapi/app_test.go +++ b/backend/secretapi/app_test.go @@ -89,6 +89,7 @@ func TestMembershipDistinctPayerProof(t *testing.T) { t.Fatalf("tx.from mismatch: %+v", quote.Tx) } assertQuoteCostEnvelope(t, quote.CostEnvelope, quote.Currency, quote.Decimals, quote.AmountAtomic) + assertRegulatoryProfileID(t, quote.RegulatoryProfileID, cfg.RegulatoryProfileID) } func TestMembershipCompanySponsorWithoutOwnerProof(t *testing.T) { @@ -1095,6 +1096,7 @@ func TestMarketplaceCheckoutBundlesMembershipAndMintsEntitlement(t *testing.T) { t.Fatalf("expected default merchant id, got %+v", quote) } assertQuoteCostEnvelope(t, quote.CostEnvelope, quote.Currency, quote.Decimals, quote.TotalAmountAtomic) + assertRegulatoryProfileID(t, quote.RegulatoryProfileID, cfg.RegulatoryProfileID) confirm := postJSONExpect[marketplaceCheckoutConfirmResponse](t, a, "/marketplace/checkout/confirm", marketplaceCheckoutConfirmRequest{ QuoteID: quote.QuoteID, @@ -1109,11 +1111,13 @@ func TestMarketplaceCheckoutBundlesMembershipAndMintsEntitlement(t *testing.T) { if confirm.Status != "entitlement_active" || confirm.EntitlementID == "" { t.Fatalf("unexpected confirm response: %+v", confirm) } + assertRegulatoryProfileID(t, confirm.RegulatoryProfileID, cfg.RegulatoryProfileID) status := getJSONExpect[membershipStatusResponse](t, a, "/secret/membership/status?wallet="+ownerAddr, http.StatusOK) if status.Status != "active" { t.Fatalf("expected active membership after bundled checkout, got %+v", status) } + assertRegulatoryProfileID(t, status.RegulatoryProfileID, cfg.RegulatoryProfileID) entitlements := getJSONExpect[marketplaceEntitlementsResponse](t, a, "/marketplace/entitlements?wallet="+ownerAddr, http.StatusOK) if len(entitlements.Entitlements) == 0 { @@ -2070,6 +2074,13 @@ func assertQuoteCostEnvelope(t *testing.T, got quoteCostEnvelope, wantCurrency s } } +func assertRegulatoryProfileID(t *testing.T, got string, want string) { + t.Helper() + if normalizeRegulatoryProfileID(got) != normalizeRegulatoryProfileID(want) { + t.Fatalf("regulatory profile mismatch: got=%s want=%s", got, want) + } +} + func signTypedData(t *testing.T, key *ecdsa.PrivateKey, typedData apitypes.TypedData) string { t.Helper() hash, _, err := apitypes.TypedDataAndHash(typedData) diff --git a/backend/secretapi/config.go b/backend/secretapi/config.go index cd4581e..dfd8300 100644 --- a/backend/secretapi/config.go +++ b/backend/secretapi/config.go @@ -14,6 +14,7 @@ type Config struct { DBPath string AllowedOrigin string DeploymentClass string + RegulatoryProfileID string MemberPollIntervalSec int IntentTTL time.Duration QuoteTTL time.Duration @@ -47,6 +48,7 @@ func loadConfig() Config { DBPath: env("SECRET_API_DB_PATH", "./secret.db"), AllowedOrigin: env("SECRET_API_ALLOWED_ORIGIN", "https://edut.ai"), DeploymentClass: normalizeDeploymentClass(env("SECRET_API_DEPLOYMENT_CLASS", "development")), + RegulatoryProfileID: normalizeRegulatoryProfileID(env("SECRET_API_REGULATORY_PROFILE_ID", defaultRegulatoryProfileID)), MemberPollIntervalSec: envInt("SECRET_API_MEMBER_POLL_INTERVAL_SECONDS", 30), IntentTTL: time.Duration(envInt("SECRET_API_INTENT_TTL_SECONDS", 900)) * time.Second, QuoteTTL: time.Duration(envInt("SECRET_API_QUOTE_TTL_SECONDS", 900)) * time.Second, @@ -79,6 +81,9 @@ func (c Config) Validate() error { if c.ChainID <= 0 { return fmt.Errorf("SECRET_API_CHAIN_ID must be positive") } + if !isKnownRegulatoryProfileID(c.RegulatoryProfileID) { + return fmt.Errorf("SECRET_API_REGULATORY_PROFILE_ID must be %s or %s", regulatoryProfileUSGeneral2026, regulatoryProfileEUAIBaseline2026) + } currency := strings.ToUpper(strings.TrimSpace(c.MintCurrency)) switch currency { case "USDC": diff --git a/backend/secretapi/config_test.go b/backend/secretapi/config_test.go index b4cec4d..90e5332 100644 --- a/backend/secretapi/config_test.go +++ b/backend/secretapi/config_test.go @@ -115,3 +115,21 @@ func TestConfigValidateRejectsProductionWhenStrictVerificationDisabled(t *testin t.Fatalf("expected production config validation failure when strict onchain verification disabled") } } + +func TestConfigValidateAllowsKnownRegulatoryProfiles(t *testing.T) { + cfg := loadConfigIsolated(t) + for _, profileID := range []string{regulatoryProfileUSGeneral2026, regulatoryProfileEUAIBaseline2026} { + cfg.RegulatoryProfileID = profileID + if err := cfg.Validate(); err != nil { + t.Fatalf("expected regulatory profile %s to validate, got %v", profileID, err) + } + } +} + +func TestConfigValidateRejectsUnknownRegulatoryProfile(t *testing.T) { + cfg := loadConfigIsolated(t) + cfg.RegulatoryProfileID = "unknown_profile" + if err := cfg.Validate(); err == nil { + t.Fatalf("expected regulatory profile validation failure") + } +} diff --git a/backend/secretapi/marketplace.go b/backend/secretapi/marketplace.go index 6a62192..49b1656 100644 --- a/backend/secretapi/marketplace.go +++ b/backend/secretapi/marketplace.go @@ -489,6 +489,7 @@ func (a *app) handleMarketplaceCheckoutQuote(w http.ResponseWriter, r *http.Requ writeJSON(w, http.StatusOK, marketplaceCheckoutQuoteResponse{ QuoteID: quote.QuoteID, MerchantID: quote.MerchantID, + RegulatoryProfileID: a.cfg.RegulatoryProfileID, Wallet: quote.Wallet, PayerWallet: quote.PayerWallet, OfferID: quote.OfferID, @@ -606,19 +607,20 @@ func (a *app) handleMarketplaceCheckoutConfirm(w http.ResponseWriter, r *http.Re if quote.ConfirmedAt != nil { if existing, existingErr := a.store.getMarketplaceEntitlementByQuote(r.Context(), quote.QuoteID); existingErr == nil { writeJSON(w, http.StatusOK, marketplaceCheckoutConfirmResponse{ - Status: "entitlement_active", - EntitlementID: existing.EntitlementID, - MerchantID: existing.MerchantID, - OfferID: existing.OfferID, - OrgRootID: existing.OrgRootID, - PrincipalID: existing.PrincipalID, - PrincipalRole: existing.PrincipalRole, - Wallet: existing.Wallet, - TxHash: existing.TxHash, - PolicyHash: existing.PolicyHash, - ActivatedAt: existing.IssuedAt.Format(time.RFC3339Nano), - AccessClass: existing.AccessClass, - AvailabilityState: existing.AvailabilityState, + Status: "entitlement_active", + EntitlementID: existing.EntitlementID, + MerchantID: existing.MerchantID, + RegulatoryProfileID: a.cfg.RegulatoryProfileID, + OfferID: existing.OfferID, + OrgRootID: existing.OrgRootID, + PrincipalID: existing.PrincipalID, + PrincipalRole: existing.PrincipalRole, + Wallet: existing.Wallet, + TxHash: existing.TxHash, + PolicyHash: existing.PolicyHash, + ActivatedAt: existing.IssuedAt.Format(time.RFC3339Nano), + AccessClass: existing.AccessClass, + AvailabilityState: existing.AvailabilityState, }) return } @@ -709,19 +711,20 @@ func (a *app) handleMarketplaceCheckoutConfirm(w http.ResponseWriter, r *http.Re } writeJSON(w, http.StatusOK, marketplaceCheckoutConfirmResponse{ - Status: "entitlement_active", - EntitlementID: ent.EntitlementID, - MerchantID: ent.MerchantID, - OfferID: ent.OfferID, - OrgRootID: ent.OrgRootID, - PrincipalID: ent.PrincipalID, - PrincipalRole: ent.PrincipalRole, - Wallet: ent.Wallet, - TxHash: ent.TxHash, - PolicyHash: ent.PolicyHash, - ActivatedAt: ent.IssuedAt.Format(time.RFC3339Nano), - AccessClass: ent.AccessClass, - AvailabilityState: ent.AvailabilityState, + Status: "entitlement_active", + EntitlementID: ent.EntitlementID, + MerchantID: ent.MerchantID, + RegulatoryProfileID: a.cfg.RegulatoryProfileID, + OfferID: ent.OfferID, + OrgRootID: ent.OrgRootID, + PrincipalID: ent.PrincipalID, + PrincipalRole: ent.PrincipalRole, + Wallet: ent.Wallet, + TxHash: ent.TxHash, + PolicyHash: ent.PolicyHash, + ActivatedAt: ent.IssuedAt.Format(time.RFC3339Nano), + AccessClass: ent.AccessClass, + AvailabilityState: ent.AvailabilityState, }) } diff --git a/backend/secretapi/marketplace_models.go b/backend/secretapi/marketplace_models.go index 4624036..6e61e27 100644 --- a/backend/secretapi/marketplace_models.go +++ b/backend/secretapi/marketplace_models.go @@ -67,6 +67,7 @@ type marketplaceQuoteLineItem struct { type marketplaceCheckoutQuoteResponse struct { QuoteID string `json:"quote_id"` MerchantID string `json:"merchant_id,omitempty"` + RegulatoryProfileID string `json:"regulatory_profile_id"` Wallet string `json:"wallet"` PayerWallet string `json:"payer_wallet,omitempty"` OfferID string `json:"offer_id"` @@ -104,19 +105,20 @@ type marketplaceCheckoutConfirmRequest struct { } type marketplaceCheckoutConfirmResponse struct { - Status string `json:"status"` - EntitlementID string `json:"entitlement_id"` - MerchantID string `json:"merchant_id,omitempty"` - OfferID string `json:"offer_id"` - OrgRootID string `json:"org_root_id,omitempty"` - PrincipalID string `json:"principal_id,omitempty"` - PrincipalRole string `json:"principal_role,omitempty"` - Wallet string `json:"wallet"` - TxHash string `json:"tx_hash"` - PolicyHash string `json:"policy_hash"` - ActivatedAt string `json:"activated_at"` - AccessClass string `json:"access_class"` - AvailabilityState string `json:"availability_state"` + Status string `json:"status"` + EntitlementID string `json:"entitlement_id"` + MerchantID string `json:"merchant_id,omitempty"` + RegulatoryProfileID string `json:"regulatory_profile_id"` + OfferID string `json:"offer_id"` + OrgRootID string `json:"org_root_id,omitempty"` + PrincipalID string `json:"principal_id,omitempty"` + PrincipalRole string `json:"principal_role,omitempty"` + Wallet string `json:"wallet"` + TxHash string `json:"tx_hash"` + PolicyHash string `json:"policy_hash"` + ActivatedAt string `json:"activated_at"` + AccessClass string `json:"access_class"` + AvailabilityState string `json:"availability_state"` } type marketplaceEntitlement struct { diff --git a/backend/secretapi/models.go b/backend/secretapi/models.go index 23e035d..912cdc9 100644 --- a/backend/secretapi/models.go +++ b/backend/secretapi/models.go @@ -68,22 +68,23 @@ type membershipQuoteRequest struct { } type membershipQuoteResponse struct { - QuoteID string `json:"quote_id"` - ChainID int64 `json:"chain_id"` - Currency string `json:"currency"` - AmountAtomic string `json:"amount_atomic"` - Decimals int `json:"decimals"` - CostEnvelope quoteCostEnvelope `json:"cost_envelope"` - Deadline string `json:"deadline"` - ContractAddress string `json:"contract_address"` - Method string `json:"method"` - Calldata string `json:"calldata"` - Value string `json:"value"` - OwnerWallet string `json:"owner_wallet,omitempty"` - PayerWallet string `json:"payer_wallet,omitempty"` - SponsorshipMode string `json:"sponsorship_mode,omitempty"` - SponsorOrgRoot string `json:"sponsor_org_root_id,omitempty"` - Tx map[string]any `json:"tx"` + QuoteID string `json:"quote_id"` + ChainID int64 `json:"chain_id"` + RegulatoryProfileID string `json:"regulatory_profile_id"` + Currency string `json:"currency"` + AmountAtomic string `json:"amount_atomic"` + Decimals int `json:"decimals"` + CostEnvelope quoteCostEnvelope `json:"cost_envelope"` + Deadline string `json:"deadline"` + ContractAddress string `json:"contract_address"` + Method string `json:"method"` + Calldata string `json:"calldata"` + Value string `json:"value"` + OwnerWallet string `json:"owner_wallet,omitempty"` + PayerWallet string `json:"payer_wallet,omitempty"` + SponsorshipMode string `json:"sponsorship_mode,omitempty"` + SponsorOrgRoot string `json:"sponsor_org_root_id,omitempty"` + Tx map[string]any `json:"tx"` } type membershipConfirmRequest struct { @@ -101,6 +102,7 @@ type membershipConfirmResponse struct { Status string `json:"status"` DesignationCode string `json:"designation_code"` DisplayToken string `json:"display_token"` + RegulatoryProfileID string `json:"regulatory_profile_id"` TxHash string `json:"tx_hash"` ActivatedAt string `json:"activated_at"` IdentityAssurance string `json:"identity_assurance_level"` @@ -112,6 +114,7 @@ type membershipStatusResponse struct { Status string `json:"status"` Wallet string `json:"wallet,omitempty"` DesignationCode string `json:"designation_code,omitempty"` + RegulatoryProfileID string `json:"regulatory_profile_id"` IdentityAssurance string `json:"identity_assurance_level,omitempty"` IdentityAttestedBy string `json:"identity_attested_by,omitempty"` IdentityAttestationID string `json:"identity_attestation_id,omitempty"` diff --git a/backend/secretapi/regulatory_profile.go b/backend/secretapi/regulatory_profile.go new file mode 100644 index 0000000..860adaf --- /dev/null +++ b/backend/secretapi/regulatory_profile.go @@ -0,0 +1,30 @@ +package main + +import "strings" + +const ( + regulatoryProfileUSGeneral2026 = "us_general_2026" + regulatoryProfileEUAIBaseline2026 = "eu_ai_act_2026_baseline" + defaultRegulatoryProfileID = regulatoryProfileUSGeneral2026 +) + +func normalizeRegulatoryProfileID(raw string) string { + value := strings.ToLower(strings.TrimSpace(raw)) + switch value { + case "", "us", "us_general", regulatoryProfileUSGeneral2026: + return regulatoryProfileUSGeneral2026 + case "eu", "eu_ai_act", regulatoryProfileEUAIBaseline2026: + return regulatoryProfileEUAIBaseline2026 + default: + return value + } +} + +func isKnownRegulatoryProfileID(value string) bool { + switch normalizeRegulatoryProfileID(value) { + case regulatoryProfileUSGeneral2026, regulatoryProfileEUAIBaseline2026: + return true + default: + return false + } +} diff --git a/docs/api/examples/marketplace.examples.md b/docs/api/examples/marketplace.examples.md index 480470e..839fa29 100644 --- a/docs/api/examples/marketplace.examples.md +++ b/docs/api/examples/marketplace.examples.md @@ -58,6 +58,7 @@ Success (`200`): ```json { "quote_id": "cq_01HZZXFQ27ZP6MP0V2R9M6V3KX", + "regulatory_profile_id": "us_general_2026", "wallet": "0x3ea6cbf98d23e2cf7b6f4f9bb1fb4f50b710f2d5", "payer_wallet": "0x2299547f6fA9A8f9b6d9aEA9F9D8A4B53C8A0e11", "offer_id": "edut.workspace.core", @@ -157,6 +158,7 @@ Success (`200`): { "status": "entitlement_active", "entitlement_id": "ent:8453:0x3ea6cbf98d23e2cf7b6f4f9bb1fb4f50b710f2d5:000001", + "regulatory_profile_id": "us_general_2026", "offer_id": "edut.workspace.core", "org_root_id": "org.acme.root", "principal_id": "human.joshua", diff --git a/docs/api/examples/secret-system.examples.md b/docs/api/examples/secret-system.examples.md index c1f87d8..8b84922 100644 --- a/docs/api/examples/secret-system.examples.md +++ b/docs/api/examples/secret-system.examples.md @@ -159,6 +159,7 @@ Success (`200`): { "quote_id": "mq_01HZZX4F8VQXJ6A57R8P3SCB2W", "chain_id": 8453, + "regulatory_profile_id": "us_general_2026", "currency": "USDC", "amount_atomic": "100000000", "decimals": 6, @@ -238,6 +239,7 @@ Success (`200`): "status": "membership_active", "designation_code": "0217073045482", "display_token": "0217-0730-4548-2", + "regulatory_profile_id": "us_general_2026", "tx_hash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "activated_at": "2026-02-17T07:33:09Z", "identity_assurance_level": "onramp_attested", @@ -269,6 +271,7 @@ Success (`200`): "status": "active", "wallet": "0x3ea6cbf98d23e2cf7b6f4f9bb1fb4f50b710f2d5", "designation_code": "0217073045482", + "regulatory_profile_id": "us_general_2026", "identity_assurance_level": "onramp_attested", "identity_attested_by": "moonpay", "identity_attestation_id": "mp_session_01JAA..." diff --git a/docs/api/marketplace.openapi.yaml b/docs/api/marketplace.openapi.yaml index 1a45033..880c686 100644 --- a/docs/api/marketplace.openapi.yaml +++ b/docs/api/marketplace.openapi.yaml @@ -222,12 +222,15 @@ components: description: If true, quote may bundle first-time membership fee into total. CheckoutQuoteResponse: type: object - required: [quote_id, wallet, offer_id, currency, amount_atomic, total_amount_atomic, decimals, cost_envelope, policy_hash, expires_at] + required: [quote_id, regulatory_profile_id, wallet, offer_id, currency, amount_atomic, total_amount_atomic, decimals, cost_envelope, policy_hash, expires_at] properties: quote_id: type: string merchant_id: type: string + regulatory_profile_id: + type: string + enum: [us_general_2026, eu_ai_act_2026_baseline] wallet: type: string payer_wallet: @@ -378,7 +381,7 @@ components: type: integer CheckoutConfirmResponse: type: object - required: [status, entitlement_id, offer_id, wallet, tx_hash] + required: [status, entitlement_id, regulatory_profile_id, offer_id, wallet, tx_hash] properties: status: type: string @@ -387,6 +390,9 @@ components: type: string merchant_id: type: string + regulatory_profile_id: + type: string + enum: [us_general_2026, eu_ai_act_2026_baseline] offer_id: type: string org_root_id: diff --git a/docs/api/secret-system.openapi.yaml b/docs/api/secret-system.openapi.yaml index decefb2..8f285ec 100644 --- a/docs/api/secret-system.openapi.yaml +++ b/docs/api/secret-system.openapi.yaml @@ -355,12 +355,15 @@ components: type: string MembershipQuoteResponse: type: object - required: [quote_id, chain_id, currency, amount_atomic, decimals, cost_envelope, deadline, contract_address] + required: [quote_id, chain_id, regulatory_profile_id, currency, amount_atomic, decimals, cost_envelope, deadline, contract_address] properties: quote_id: type: string chain_id: type: integer + regulatory_profile_id: + type: string + enum: [us_general_2026, eu_ai_act_2026_baseline] currency: type: string enum: [USDC] @@ -469,7 +472,7 @@ components: description: Optional provider attestation reference id. MembershipConfirmResponse: type: object - required: [status, designation_code, display_token, tx_hash, activated_at, identity_assurance_level] + required: [status, designation_code, display_token, regulatory_profile_id, tx_hash, activated_at, identity_assurance_level] properties: status: type: string @@ -478,6 +481,9 @@ components: type: string display_token: type: string + regulatory_profile_id: + type: string + enum: [us_general_2026, eu_ai_act_2026_baseline] tx_hash: type: string activated_at: @@ -492,7 +498,7 @@ components: type: string MembershipStatusResponse: type: object - required: [status] + required: [status, regulatory_profile_id] properties: status: type: string @@ -501,6 +507,9 @@ components: type: string designation_code: type: string + regulatory_profile_id: + type: string + enum: [us_general_2026, eu_ai_act_2026_baseline] identity_assurance_level: type: string enum: [none, crypto_direct_unattested, sponsored_unattested, onramp_attested] diff --git a/docs/secret-system-spec.md b/docs/secret-system-spec.md index 845c017..74a48ae 100644 --- a/docs/secret-system-spec.md +++ b/docs/secret-system-spec.md @@ -258,6 +258,7 @@ Response: { "quote_id": "mq_...", "chain_id": 8453, + "regulatory_profile_id": "us_general_2026", "currency": "USDC", "amount_atomic": "100000000", "decimals": 6, @@ -314,11 +315,14 @@ Response: "status": "membership_active", "designation_code": "0217073045482", "display_token": "0217-0730-4548-2", + "regulatory_profile_id": "us_general_2026", "tx_hash": "0x...", "activated_at": "2026-02-17T07:33:09Z" } ``` +`regulatory_profile_id` binds quote/activation responses to deployment policy posture (`us_general_2026` or `eu_ai_act_2026_baseline`). + ## Security Controls 1. Intent TTL and one-time nonce consumption.