Laminar Field Manual
The complete operational guide for treasurers processing batch disbursements using Laminar. This manual covers the core workflow, data formats, validation rules, and troubleshooting procedures.
Overview
Laminar is the operational command console for the Zcash economy. It is a local-first, air-gapped terminal for high-volume Zcash batch operations.
Laminar separates Transaction Construction (the Brain) from Key Storage (the Muscle). Treasurers construct batches on a desktop application and hand off signing to an offline mobile wallet device without private keys ever touching an internet-connected machine.
Key Properties
Non-Custodial
Never accesses, stores, or transmits private keys or seed phrases
Stateless
No server-side state. All data persists locally in encrypted IndexedDB
Air-Gapped
Transaction construction requires no network access. QR handoff bridges the gap
Deterministic
Same input CSV always produces identical output. No randomness in construction
Auditable
JSON receipts provide machine-readable proof of constructed transactions
Open Source
MIT / Apache 2.0 dual-license. Full source available for audit and verification
Core Concepts
Understanding the architecture and philosophy behind Laminar's design.
The Spreadsheet Gap
Zcash organizations currently manage treasury disbursements using a fragile, manual workflow: payroll data in spreadsheets, manual copy-paste of each address into a mobile wallet, transactions signed one-by-one, and no standardized audit trail.
Laminar closes this gap by accepting batch input and generating scannable QR codes that mobile wallets can process, reducing a 2-4 hour process to under 5 minutes for 50+ recipients.
Brain vs. Muscle
Brain (Laminar Terminal)
Handles computation, validation, ZIP-321 construction, and QR generation. Runs on internet-connected desktop. Has no signing authority.
Muscle (Mobile Wallet)
Stores private keys, signs transactions, broadcasts to network. Zashi or YWallet. Keys never leave the device.
Payment Intents
Laminar constructs Payment Intents, not transactions. A Payment Intent contains all information needed to create a transaction (recipients, amounts, memos) but has no signing authority. The mobile wallet receives the intent and executes it using locally-stored keys.
Critical Invariants
Architectural constraints that are enforced at all times. Violation of any invariant is a critical defect.
These invariants are architectural constraints enforced at all times. Violation of any invariant is a critical defect.
- INV-01 Laminar NEVER stores, generates, or requests spending keys or seed phrases.
- INV-02 Laminar NEVER signs transactions. It constructs Payment Intents only.
- INV-03 Laminar NEVER broadcasts transactions. The mobile wallet handles network communication.
- INV-04 ALL monetary arithmetic uses integer zatoshis (bigint). Floating-point is PROHIBITED.
- INV-05 All cross-boundary data MUST be validated against versioned schemas.
- INV-06 Sensitive IndexedDB fields (memos, labels) MUST be encrypted with session key.
- INV-07 Memo encoding: UTF-8 bytes to standard base64. Maximum 512 bytes.
- INV-08 Handoff results MUST conform to HandoffResult schema.
- INV-09 No telemetry. No analytics. No external network requests.
- INV-10 If ANY row fails validation, the ENTIRE batch is rejected. No partial processing.
Workflow
The standard batch disbursement workflow consists of seven steps, from CSV preparation to receipt generation.
-
Prepare CSV
Create a CSV file with recipient addresses, amounts, and optional memos. Use UTF-8 encoding. See CSV Format for schema details.
-
Import Batch
Drag the CSV file into Laminar Terminal or use the file picker. Select the network (Mainnet or Testnet) before import.
-
Review & Validate
Laminar validates every row: address format, network match, amount bounds, memo length. Review the batch summary showing total ZEC and recipient count. If any row fails, the entire batch is rejected.
-
Generate QR
Click "Proceed" to construct ZIP-321 payment requests. Laminar generates static QR (small batches) or animated UR sequence (large batches) automatically.
-
Scan with Wallet
Open Zashi or YWallet on your mobile device. Use the scan function to capture the QR code. For animated sequences, hold steady until all frames are captured.
-
Sign & Broadcast
Review the transaction details on your mobile wallet. Confirm to sign with your locally-stored keys. The wallet broadcasts to the Zcash network.
-
Save Receipt
Return to Laminar and click "Generate Receipt". Save the JSON receipt bundle for your records. This provides an audit trail of the constructed batch.
CSV Format
Laminar accepts CSV files with flexible column naming. The first row must contain headers. Files must use UTF-8 encoding.
Column Specification
| Header | Aliases | Required | Description |
|---|---|---|---|
| address | recipient, to |
Yes | Zcash address (Unified, Sapling, or Transparent) |
| amount | value, zec |
Yes* | Amount in ZEC (decimal notation) |
| amount_zatoshis | zatoshis, zats |
Yes* | Amount in zatoshis (integer) |
| memo | message, note |
No | Encrypted memo (max 512 bytes UTF-8) |
| label | name, recipient_name |
No | Display label (not sent on-chain) |
*Either amount OR amount_zatoshis is required.
If both are present, amount_zatoshis takes precedence.
Example CSV
address,amount,memo,label
u1abc123...,10.5,"January payment",Alice
u1def456...,25.0,"January payment",Bob
u1ghi789...,15.75,,Carol
Encoding Requirements
- File encoding: UTF-8 (with or without BOM)
- Line endings: LF or CRLF
- Delimiter: Comma (,)
- Quote character: Double quote (")
- Escape: Doubled double quotes ("")
CSV fields beginning with =, +, -,
@, \t, or \r are detected and rejected
to prevent formula injection attacks.
JSON Format
For programmatic batch generation, Laminar accepts JSON files conforming to the Laminar Batch Schema.
Input Schema
{
"version": "1.0",
"network": "mainnet",
"recipients": [
{
"address": "u1...",
"amount_zatoshis": 100000000,
"memo": "January payment",
"label": "Alice"
}
]
}
Receipt Output Schema
{
"laminar_version": "1.0.0",
"timestamp": "2025-01-28T12:00:00Z",
"batch_id": "a1b2c3d4",
"network": "mainnet",
"total_zatoshis": 5000000000,
"total_zec": "50.0",
"recipient_count": 25,
"zip321_payload_hash": "sha256:...",
"segments": 1
}
Validation Rules
All data is validated before batch construction. If any row fails, the entire batch is rejected.
Address Validation
- Format: Valid Zcash encoding (Bech32m for Unified, Bech32 for Sapling, Base58Check for Transparent)
- Network: Must match configured network. Cross-network rejected.
- Types: Unified, Sapling, and Transparent addresses accepted
Amount Validation
- Minimum: 1 zatoshi (0.00000001 ZEC)
- Maximum: 21,000,000 ZEC (total supply cap)
- Dust: Warning below 10,000 zats (0.0001 ZEC)
- Precision: Up to 8 decimal places
Memo Validation
- Maximum: 512 bytes UTF-8 encoded
- Encoding: Valid UTF-8 required
- Empty: Allowed
Batch Validation
- Max recipients: 500 per batch
- Overflow: Sum must not exceed u64 max
- Duplicates: Warning only (may be intentional)
QR Generation
Laminar automatically selects between static QR and animated UR sequences based on payload size.
Static QR (ZIP-321)
For payloads under 2,510 bytes (with safety margin), Laminar generates a single static QR code containing a ZIP-321 payment request URI.
Animated UR Sequences
For larger payloads, Laminar uses Uniform Resources (UR) encoding to create animated QR sequences that transmit data across multiple frames.
ZIP-321 URI Format
zcash:<address>?amount=<zec>&memo=<base64>
&address.1=<addr>&amount.1=<zec>&memo.1=<base64>
UR Animation Parameters
Fragment Size
Bytes per frame (100-200 range)
Frame Rate
100ms per frame display
Encoding
Blockchain Commons UR standard
Hold your phone steady during animated QR scanning. Ensure adequate lighting and avoid glare on the screen. The progress indicator shows capture status.
Payload Limits
Understanding size constraints for each handoff mode.
Size Limits by Mode
Static QR
Bytes effective (2,953 raw, 15% margin)
Animated UR
Effective limit (32 KB raw, 10% margin)
Deep Link
Effective limit (8 KB raw, 10% margin)
Recipient Capacity
| Mode | Typical Capacity | Notes |
|---|---|---|
| Static QR (ZIP-321) | 8-12 recipients | Depends on memo length |
| Animated UR | 50+ recipients | ~15-20 frames at 100ms |
| Auto Batch Split | Unlimited | Sequential segments |
Wallet Compatibility
Tested wallet versions and supported features. Results tracked by version.
| Wallet | Version | Static QR | Animated UR | Deep Link | Notes |
|---|---|---|---|---|---|
| Zashi iOS | 1.2+ | Yes | Yes | Yes | Primary target |
| Zashi Android | 1.2+ | Yes | Yes | Yes | Primary target |
| YWallet iOS | 1.5+ | Yes | No | Yes | Use batch splitting |
| YWallet Android | 1.5+ | Yes | No | Yes | Use batch splitting |
Error Codes
All errors use standardized codes. No string literals in error handling.
| Code | Name | Description |
|---|---|---|
| 1001 | INVALID_ADDRESS | Address does not match any valid Zcash encoding |
| 1002 | INVALID_AMOUNT | Amount format is invalid or cannot be parsed |
| 1003 | AMOUNT_OUT_OF_RANGE | Amount is zero, negative, or exceeds maximum |
| 1004 | MEMO_TOO_LONG | Memo exceeds 512 bytes when UTF-8 encoded |
| 1008 | CSV_PARSE_ERROR | CSV file is malformed or uses unsupported encoding |
| 1009 | CSV_FORMULA_INJECTION | Field begins with formula character (=, +, -, @) |
| 1010 | NETWORK_MISMATCH | Address network does not match configured network |
| 1012 | MISSING_REQUIRED_COLUMN | Required column (address or amount) not found |
| 1013 | BATCH_TOTAL_OVERFLOW | Sum of all amounts exceeds u64 maximum |
| 3001 | DB_UNAVAILABLE | IndexedDB not available or access denied |
| 3003 | ENCRYPTION_FAILED | Failed to encrypt data for storage |
| 5003 | PAYLOAD_TOO_LARGE | Payload exceeds maximum size for selected mode |
| 5008 | UR_ENCODING_FAILED | Failed to encode payload as Uniform Resource |
| 9999 | UNKNOWN | Unknown or unclassified error |
Constants Reference
Critical constants used throughout the system. Reference implementations must use exact values.
Monetary
ZATOSHI_PER_ZEC = 100,000,000
ZATOSHI_MIN = 1
ZATOSHI_MAX = 2.1e15
DUST_THRESHOLD = 10,000
Payload Limits
QR_STATIC = 2510
QR_ANIMATED = 29000
DEEPLINK = 7200
Encoding
MEMO_MAX = 512
MAX_RECIPIENTS = 500
UR_FRAGMENT = 150
UR_FRAME_MS = 100
Glossary
Key terms and definitions used throughout Laminar documentation.
| Term | Definition |
|---|---|
| Air Gap | Physical or logical separation between systems to prevent data transfer except through controlled channels (QR codes). |
| Handoff | The process of transferring a Payment Intent from Laminar to a mobile wallet via QR code. |
| Iron Core | Codename for Laminar's foundational Rust library containing all business logic. |
| Operator | The person using Laminar to construct batch transactions (treasurer, finance admin). |
| Payment Intent | A constructed but unsigned transaction request. Contains all information needed to sign but no signing authority. |
| Recipient | A single entry in a batch: address, amount, and optional memo. |
| Tactical Spike | Phase 1 of Laminar development, focused on proving the core workflow with minimal features. |
| Transaction Intent | See Payment Intent. Protocol-native terminology. |
| UR | Uniform Resources. Encoding standard for transmitting data via animated QR code sequences. |
| Vault | Local encrypted storage for drafts and address book. Not custody. |
| Zatoshi | The smallest unit of ZEC. 1 ZEC = 100,000,000 zatoshis. |
| ZIP-321 | Zcash Improvement Proposal defining the standard format for payment request URIs. |