Loading module
Resolving locale, route permissions, and workspace projection.
Resolving locale, route permissions, and workspace projection.
მიმდინარე არეალი: სტუმარი
კატეგორია: 60_technical_specs | ვერსია: v1.0.0
მფლობელი: DOCUMENT_CUSTODIAN | გადახედვის ციკლი: 60 დღე
დამტკიცების უფლებამოსილება: GOVERNANCE_ADMIN, COMPLIANCE_OFFICER
დოკუმენტაციის პორტალი მხოლოდ წაკითხვადია. რედაქტირება და ცვლილების endpoint-ები გამორთულია.
Kvary პლატფორმა თავდაპირველად ქართულ ენაზეა შექმნილი. სადაც ქართული ვერსია არსებობს, პლატფორმის UI-ის, დოკუმენტაციისა და იურიდიული განმარტების ავტორიტეტული ენა არის ქართული.
სხვა ენებზე თარგმანები მოცემულია მოხერხებულობისთვის. კონკრეტული ჩანაწერი ან flow შეიძლება სხვა ენაზე იყოს წარმოშობილი და ჰქონდეს საკუთარი source/legal locale, თუმცა სადაც ქართული ვერსია ხელმისაწვდომია, პლატფორმის დონის ფორმულირებასა და განმარტებაში უპირატესობა ქართულ ვერსიას ენიჭება.
მეტამონაცემები არასრულია: Document ID, Version, Owner Role, Last Review Date, Next Review Date, Change Log
Status: DRAFT
Date: 2026-04-20
Scope: Spec/design batch only
Audit Basis: code-verified repository inspection
This specification proposes a governance-first Real Asset Token Issuance Wizard that fits the strongest existing product patterns in the repo instead of introducing a naive "mint token" form.
The design is grounded in these confirmed repo realities:
currentGate, missingRequirements, and prefilled payloads instead of optimistic direct publishing.The result is a wizard that should:
A direct mint form would be wrong for this repo for six reasons:
In short: the correct product is not "create token." The correct product is "structure an issuance against a verified asset basis and declare it only after governance gates are satisfied."
The wizard should be modeled in three layers:
asset_selectionsourceAssetTypeprimaryRefIdownerStakeholderIdrightsBasisregistryTruthStatusjurisdictioncapacitySnapshot.remainingEligibleQuantityparcelIdallocationIddeclarationIdinspectionIdreceivableIdinventoryLotIdwarehouseReceiptIdseasonlinked map/spatial referenceSourceAssetReferenceeligibility_checklistpolicyVersiondecisionEligibilityChecklistELIGIBLE or policy-allowed CONDITIONALLY_ELIGIBLEasset_structuringmaxIssuableAmountrisk_reserve_configurationrequirementLevelreserveTypeminimumReserveRatioconfiguredReserveRatiohaircutPercentReserveConfigurationinsurance_layerrequirementLevelInsuranceConfigurationfactoring_financing_layermodeNONE: financier, facility reference, advance rate, maximum finance amount, recourse type, priority rank, assignment evidenceFactoringConfigurationissuance_termsissuanceClassunitNameunitCurrencyfaceValuePerUnitunitCounttotalIssuanceAmountclaimRightsredemptionModelissuanceDateTokenIssuanceTermsreview_and_issueREADY_FOR_REVIEW, then APPROVED_FOR_ISSUE, then ISSUEDThese DTOs are intentionally chain-neutral. They describe governance, readiness, and issuance structure first, and leave live blockchain binding out of scope.
export type IssuanceWizardStepId =
| "asset_selection"
| "eligibility_checklist"
| "asset_structuring"
| "risk_reserve_configuration"
| "insurance_layer"
| "factoring_financing_layer"
| "issuance_terms"
| "review_and_issue";
export type IssuanceDraftState =
| "DRAFT"
| "STRUCTURING"
| "READY_FOR_REVIEW"
| "APPROVED_FOR_ISSUE"
| "ISSUED"
| "SUSPENDED"
| "MATURED"
| "REDEEMED"
| "CLOSED"
| "CANCELLED";
export type ReadinessSeverity = "BLOCKER" | "WARNING" | "INFO";
export type EvidenceReference = Readonly<{
kind: string;
refId: string;
label?: string;
uri?: string;
sha256?: string;
verified?: boolean;
uploadedAt?: string;
expiresAt?: string;
}>;
export type ReadinessFinding = Readonly<{
code: string;
severity: ReadinessSeverity;
stepId: IssuanceWizardStepId;
message: string;
path?: string;
sourceRefId?: string;
evidenceRefId?: string;
policyRef?: string;
expiresAt?: string;
suggestedAction?: string;
}>;
export type SourceAssetType =
| "PARCEL"
| "OUTPUT_ALLOCATION"
| "DECLARATION"
| "INSPECTION_CERTIFICATE"
| "RECEIVABLE"
| "INVENTORY_LOT";
export type RightsBasis =
| "OWNERSHIP"
| "LEASE"
| "ASSIGNMENT"
| "RECEIVABLE_ASSIGNMENT"
| "WAREHOUSE_CUSTODY";
export type RegistryTruthStatus =
| "VERIFIED"
| "PENDING_REVIEW"
| "DISPUTED"
| "EXTERNAL_ONLY";
export type SourceAssetCapacitySnapshot = Readonly<{
grossQuantity?: number;
unit?: string;
declaredQuantity?: number;
reservedQuantity?: number;
remainingEligibleQuantity?: number;
approvedLimitQuantity?: number;
requestedIssuanceQuantity?: number;
}>;
export type SourceAssetReference = Readonly<{
sourceAssetType: SourceAssetType;
primaryRefId: string;
parcelId?: string;
productiveAssetId?: string;
allocationId?: string;
declarationId?: string;
inspectionId?: string;
receivableId?: string;
inventoryLotId?: string;
ownerStakeholderId: string;
operatorStakeholderId?: string;
rightsBasis: RightsBasis;
registryTruthStatus: RegistryTruthStatus;
jurisdiction: string;
region?: string;
season?: string;
sourceState?: string;
assetSnapshot: Readonly<{
label?: string;
productCategory?: string;
quantity?: number;
unit?: string;
qualityGrade?: string;
originRegion?: string;
warehouseReceiptId?: string;
}>;
capacitySnapshot: SourceAssetCapacitySnapshot;
linkedEvidence: readonly EvidenceReference[];
}>;
export type EligibilityChecklistItem = Readonly<{
code: string;
label: string;
satisfied: boolean;
blocking: boolean;
detail?: string;
sourceDomain?:
| "PARCEL"
| "DECLARATION"
| "INSPECTION"
| "RESERVE"
| "INSURANCE"
| "FINANCING";
expiresAt?: string;
waiverAllowed?: boolean;
}>;
export type EligibilityDecision =
| "ELIGIBLE"
| "CONDITIONALLY_ELIGIBLE"
| "INELIGIBLE";
export type EligibilityChecklist = Readonly<{
policyVersion: string;
evaluatedAt?: string;
evaluatedBy?: string;
decision: EligibilityDecision;
linkedInspectionStatus?:
| "NOT_REQUIRED"
| "CURRENT"
| "EXPIRING"
| "EXPIRED"
| "FAILED";
linkedDeclarationStatus?: string;
evidenceCoverageStatus?: "COMPLETE" | "PARTIAL" | "MISSING";
items: readonly EligibilityChecklistItem[];
}>;
export type AssetStructuringSnapshot = Readonly<{
valuationBasis: Readonly<{
method:
| "REGISTRY_SNAPSHOT"
| "INSPECTION_VERIFIED"
| "RECEIVABLE_FACE_VALUE"
| "MARKET_REFERENCE"
| "INVENTORY_REPORT";
amount: number;
currency: string;
valuationDate: string;
providerStakeholderId?: string;
reportRefId?: string;
}>;
requestedIssuanceAmount: number;
requestedIssuanceCurrency: string;
requestedIssuanceQuantity?: number;
haircutPercent: number;
riskBufferPercent?: number;
overcollateralizationPercent?: number;
maxIssuableAmount: number;
claimBasis:
| "OWNERSHIP_BENEFICIAL_INTEREST"
| "OUTPUT_PROCEEDS"
| "RECEIVABLE_COLLECTION_RIGHT"
| "INVENTORY_RELEASE_RIGHT";
servicingPartyId?: string;
}>;
export type ReserveRequirementLevel = "NONE" | "OPTIONAL" | "REQUIRED";
export type ReserveConfiguration = Readonly<{
requirementLevel: ReserveRequirementLevel;
reserveType:
| "CASH_ESCROW"
| "COLLATERAL_BUFFER"
| "THIRD_PARTY_GUARANTEE"
| "HYBRID";
minimumReserveRatio: number;
configuredReserveRatio: number;
haircutPercent: number;
overcollateralizationPercent?: number;
reserveAssetKind?:
| "FIAT"
| "TOKENIZED_CASH"
| "SOURCE_ASSET_BUFFER"
| "GUARANTEE";
holderPartyId?: string;
evidenceRefs: readonly EvidenceReference[];
replenishmentPolicy?: string;
breachAction?: "BLOCK_ISSUANCE" | "SUSPEND" | "TOP_UP_REQUIRED";
}>;
export type InsuranceConfiguration = Readonly<{
requirementLevel: ReserveRequirementLevel;
coverageStatus:
| "NOT_SELECTED"
| "PENDING_BINDER"
| "ACTIVE"
| "EXPIRED";
providerStakeholderId?: string;
reinsurancePartnerStakeholderId?: string;
policyReference?: string;
coverageType?:
| "CROP"
| "PROPERTY_DAMAGE"
| "TITLE_RISK"
| "CREDIT"
| "PARAMETRIC"
| "MULTI_PERIL";
insuredValue?: number;
currency?: string;
deductiblePercent?: number;
coverageStartAt?: string;
coverageEndAt?: string;
claimBeneficiaryPartyId?: string;
exclusionsNote?: string;
evidenceRefs: readonly EvidenceReference[];
}>;
export type FactoringConfiguration = Readonly<{
mode:
| "NONE"
| "RECEIVABLE_FACTORING"
| "INVENTORY_FINANCE"
| "PREPAYMENT_ADVANCE";
financierStakeholderId?: string;
facilityReference?: string;
advanceRatePercent?: number;
maximumFinanceAmount?: number;
currency?: string;
recourseType?: "WITH_RECOURSE" | "WITHOUT_RECOURSE";
priorityRank?: "SENIOR" | "PARI_PASSU" | "SUBORDINATED";
settlementWaterfall?: readonly string[];
maturityDate?: string;
assignmentEvidenceRefs: readonly EvidenceReference[];
notes?: string;
}>;
export type TransferRestriction = Readonly<{
kind:
| "PERMISSIONED_ONLY"
| "JURISDICTION_LIMITED"
| "LOCKUP"
| "INVESTOR_TYPE_LIMIT";
value?: string;
until?: string;
}>;
export type TokenIssuanceTerms = Readonly<{
issuanceClass: string;
internalSymbol?: string;
unitName: string;
unitCurrency: string;
faceValuePerUnit: number;
unitCount: number;
totalIssuanceAmount: number;
claimRights: readonly (
| "PRINCIPAL"
| "PROCEEDS"
| "RESERVE_ACCESS"
| "INSURANCE_PROCEEDS"
| "RECOVERY_ACTION"
)[];
redemptionModel:
| "AT_MATURITY"
| "AMORTIZING"
| "REVOLVING_POOL"
| "EVENT_DRIVEN";
issuanceDate: string;
maturityDate?: string;
redemptionWindowStart?: string;
redemptionWindowEnd?: string;
transferRestrictions: readonly TransferRestriction[];
publicationMode:
| "INTERNAL_REGISTRY"
| "DECLARED_PUBLICLY"
| "PRIVATE_PLACEMENT";
servicingNotes?: string;
downstreamBindingMode: "DECLARATIVE_ONLY" | "FUTURE_CHAIN_BINDING";
}>;
export type ApprovalRecord = Readonly<{
stage:
| "READINESS_REVIEW"
| "ISSUANCE_APPROVAL"
| "ISSUE_EXECUTION"
| "SUSPENSION"
| "AMENDMENT";
actorId: string;
role: string;
decidedAt: string;
decision: "APPROVED" | "CHANGES_REQUIRED" | "REJECTED" | "EXECUTED";
note?: string;
}>;
export type IssuanceReadinessStepStatus = Readonly<{
stepId: IssuanceWizardStepId;
ready: boolean;
blockerCount: number;
warningCount: number;
infoCount: number;
lastEvaluatedAt?: string;
}>;
export type IssuanceReadinessReport = Readonly<{
draftId: string;
evaluatedAt: string;
evaluatedBy?: string;
policyVersion: string;
overallStatus:
| "NOT_READY"
| "READY_WITH_WARNINGS"
| "READY_FOR_REVIEW"
| "APPROVED_FOR_ISSUE";
hardBlockers: readonly ReadinessFinding[];
warnings: readonly ReadinessFinding[];
infoNotes: readonly ReadinessFinding[];
stepStatuses: readonly IssuanceReadinessStepStatus[];
immutableSnapshotHash?: string;
requiredApprovals: readonly (
| "READINESS_REVIEW"
| "ISSUANCE_APPROVAL"
| "ISSUE_EXECUTION"
)[];
}>;
export type RealAssetIssuanceDraft = Readonly<{
draftId: string;
state: IssuanceDraftState;
currentStep: IssuanceWizardStepId;
sourceAsset?: SourceAssetReference;
eligibility?: EligibilityChecklist;
structuring?: AssetStructuringSnapshot;
reserveConfiguration?: ReserveConfiguration;
insuranceConfiguration?: InsuranceConfiguration;
factoringConfiguration?: FactoringConfiguration;
issuanceTerms?: TokenIssuanceTerms;
readinessReport?: IssuanceReadinessReport;
evidenceCatalog: readonly EvidenceReference[];
governance: Readonly<{
createdBy: string;
lastEditedBy?: string;
rulesVersion: string;
sourceSystem: string;
amendmentOfDraftId?: string;
approvals: readonly ApprovalRecord[];
}>;
createdAt: string;
updatedAt: string;
submittedForReviewAt?: string;
approvedForIssueAt?: string;
issuedAt?: string;
suspendedAt?: string;
closedAt?: string;
}>;
READY_FOR_REVIEW| Condition | Default severity | Notes | | --- | --- | --- | | Ownership not verified | Hard blocker | Source truth failure | | Parcel/source record not registry-linked | Hard blocker | Unless policy explicitly permits external-only basis | | Remaining eligible quantity exhausted | Hard blocker | Prevents over-issuance | | Inspection expired | Hard blocker if required, warning otherwise | Policy-driven | | Inspection failed | Hard blocker | Eligibility failure | | Mandatory evidence missing | Hard blocker | Includes proof of title/assignment/reserve funding | | Insurance missing | Warning if optional, hard blocker if required | Policy-driven | | Reserve ratio below minimum | Hard blocker | Deterministic numeric gate | | Reserve evidence stale | Warning, escalates to blocker if policy requires current attestation | Policy-driven | | Factoring terms incomplete while financing selected | Hard blocker | Selected structure not fully defined | | Valuation stale | Warning or hard blocker depending on age threshold | Policy-driven | | Transfer restriction not yet implementable | Warning | Allowed only if declaration is still internal | | Snapshot drift after approval | Hard blocker | Must force re-review |
READY_FOR_REVIEW, but approval must explicitly accept or return those warnings.DRAFT: Draft exists, source asset may still be incomplete.STRUCTURING: Source asset chosen and structuring work is underway.READY_FOR_REVIEW: Draft frozen for governance review; no hard blockers remain.APPROVED_FOR_ISSUE: Governance approval granted; economics and claim terms frozen.ISSUED: Issuance event recorded.SUSPENDED: Issuance is paused due to reserve breach, evidence lapse, insurance lapse, or governance hold.MATURED: Economic maturity reached.REDEEMED: Redemption completed.CLOSED: Administrative close-out complete.CANCELLED: Withdrawn before issue.| From | To | Trigger | Mandatory constraints |
| --- | --- | --- | --- |
| DRAFT | STRUCTURING | Source asset basis selected | source asset snapshot exists |
| STRUCTURING | READY_FOR_REVIEW | Mark ready for review | zero hard blockers; snapshot frozen |
| READY_FOR_REVIEW | STRUCTURING | Changes requested | reviewer note recorded |
| READY_FOR_REVIEW | APPROVED_FOR_ISSUE | Governance approval | separation of duties satisfied |
| APPROVED_FOR_ISSUE | ISSUED | Issue action executed | immutable snapshot hash matches approved record |
| ISSUED | SUSPENDED | Breach/lapse/policy hold | suspension reason recorded |
| SUSPENDED | ISSUED | Reinstatement approved | amendment/reinstatement evidence recorded |
| ISSUED | MATURED | Maturity reached | maturity rule satisfied |
| MATURED | REDEEMED | Redemption recorded | redemption evidence recorded |
| REDEEMED | CLOSED | Closure completed | archival/close-out record present |
| DRAFT or STRUCTURING or READY_FOR_REVIEW | CANCELLED | Cancellation recorded | actor authority and reason recorded |
APPROVED_FOR_ISSUE or ISSUED.SUSPENDED, MATURED, REDEEMED, and CLOSED belong to later operational phases, but the states should exist now so issuance terms can be designed against them.Allowed initiators should be:
LAND_OWNER or other authorized asset originator when the asset basis belongs to themISSUANCE_OPERATOR acting on behalf of the originatorNot allowed:
Editable by:
ISSUANCE_OPERATORRISK_OFFICERFINANCE_OPERATORConstraints:
Minimum proposed separation of duties:
READINESS_REVIEWER verifies no blockers remainISSUANCE_APPROVER grants issue approvalISSUE_EXECUTOR records the issuance event against the approved snapshotAdditional constraint:
After ISSUED, these fields must be immutable:
These must use amendment flow, never direct edit:
These may be append-only operational events instead of amendments:
The wizard should eventually consume parcel/land-owner truth as a source-asset resolver, not as free-form user input.
Implications:
missingRequirements and gate logic should prefilter whether a parcel-origin issuance can even startInspection is not an afterthought. It belongs in eligibility gating.
Implications:
This wizard should reuse the declaration-backed mutation pattern already proven in the repo.
That means future implementation should follow:
This keeps issuance aligned with the current durable internal/admin write model rather than the repo's weaker public action surfaces.
Factoring and financing should be declared now but executed later.
Implications:
Reserve attestations and insurance binders should follow the same evidence-reference discipline already used in declaration flows:
This should launch first as an internal/admin governed wizard, not as a public self-service mint surface.
That matches the repo's strongest current truth:
packages/core or a new packages/core/issuance slice, using the DTOs above.blockingErrors, warnings, and readinessChecklist equivalents for issuance.apps/web by reusing current declaration wizard/readiness panel patterns.What should not be implemented first:
ISSUANCE WIZARD SPEC READY
The spec is ready to guide implementation because the required seams are now explicit:
Known implementation risks remain, but they do not block the spec itself:
Those are implementation-order constraints, not reasons to revert to a direct mint form.