Decentralized land registration and verification on the Stellar blockchain.
Landbou is an open-source, decentralized protocol for registering, verifying, and trading land parcels on the Stellar blockchain. It provides governments, developers, and land owners with a transparent, immutable, and tamper-proof system for managing real-world land records.
By combining Stellar's fast, low-cost infrastructure with Soroban smart contracts, Landbou eliminates the inefficiencies of paper-based land registries and bridges the gap between traditional property law and Web3 ownership.
| Feature | Description |
|---|---|
| Land Registration | Register parcels on-chain with GPS coordinates, area, and land-use metadata |
| Inspector Workflow | Assigned inspectors approve or reject parcels before they can be traded |
| NFT Ownership | Each approved parcel is minted as a non-fungible token (Soroban NFT) |
| Marketplace | List, update, cancel, and purchase land parcels using XLM |
| Transaction History | Full immutable audit trail for every ownership change |
| Role Management | Admin, inspector, and owner roles with on-chain access control |
| Upgradeable Contracts | Admin-gated Wasm upgrade path via Soroban's update_current_contract_wasm |
Landbou is organized as a monorepo with the following components:
landbou/
├── app/
│ └── client/ # Next.js frontend (Stellar wallet integration)
├── contracts/
│ └── land_registry/ # Soroban smart contracts (Rust)
│ ├── src/
│ │ ├── lib.rs # Module root & public re-exports
│ │ ├── land_register.rs # LandRegistry contract
│ │ ├── land_nft.rs # LandNFT contract
│ │ ├── types.rs # Shared data structures
│ │ ├── errors.rs # Error code definitions
│ │ └── utils.rs # Land ID generation & helpers
│ ├── tests/
│ │ └── test_land_registry.rs
│ └── Cargo.toml
├── docs/ # Protocol documentation
├── indexers/
│ └── land-registry/ # Blockchain event indexer (Node.js)
├── land-registry-backend/ # REST API backend (Express + PostgreSQL)
├── landing_page/ # Marketing & onboarding site (Next.js)
├── sdk/ # JavaScript SDK for protocol interaction
└── tools/
├── cli/ # CLI tool for contract interaction (Rust)
└── explorer/ # Block explorer utility (Rust)
┌────────────────────┐ mints / locks / unlocks ┌──────────────┐
│ LandRegistry │ ──────────────────────────────────► │ LandNFT │
│ │ │ │
│ • register_land │ cross-contract calls │ • mint │
│ • approve_land │ ◄────────────────────────────────── │ • transfer │
│ • buy_land │ │ • lock │
│ • … │ │ • unlock │
└────────────────────┘ └──────────────┘
│
│ reads / writes
▼
Soroban Persistent Storage
(lands, listings, inspectors, history)
The contracts are written in Rust using the soroban-sdk and live in contracts/land_registry/.
The central registry contract. It handles the full lifecycle of a land parcel.
| Function | Access | Description |
|---|---|---|
initialize(admin, nft_contract) |
Once | Deploy and wire up the contracts |
register_land(caller, location, area, land_use) |
Any | Register a new land parcel; mints a locked NFT |
transfer_land(caller, land_id, new_owner) |
Owner | Transfer approved land to a new owner |
get_land(land_id) |
View | Fetch a land parcel by ID |
get_land_count() |
View | Total number of registered parcels |
get_lands_by_owner(owner) |
View | List of land IDs owned by an address |
get_all_lands() |
View | Full list of all registered parcels |
update_land(caller, land_id, area, land_use, status) |
Owner | Update mutable parcel fields |
approve_land(caller, land_id) |
Inspector | Approve a pending parcel; unlocks NFT |
reject_land(caller, land_id) |
Inspector | Reject a pending parcel |
is_inspector(inspector) |
View | Check if address is a registered inspector |
is_land_approved(land_id) |
View | Check if a parcel has been approved |
get_land_status(land_id) |
View | Get current LandStatus of a parcel |
get_pending_approvals() |
View | List of land IDs awaiting approval |
get_land_transaction_history(land_id) |
View | Full ownership history for a parcel |
set_land_inspector(caller, land_id, inspector) |
Admin | Assign an inspector to a parcel |
get_land_inspector(land_id) |
View | Get the assigned inspector for a parcel |
add_inspector(caller, inspector) |
Admin | Register a new inspector address |
remove_inspector(caller, inspector) |
Admin | Remove a registered inspector |
get_all_inspectors() |
View | List of all registered inspectors |
inspector_lands(inspector) |
View | List of parcels assigned to an inspector |
get_user_type(user_address) |
View | Returns "admin", "inspector", "owner", or "none" |
create_listing(caller, land_id, price) |
Owner | Create a marketplace listing |
cancel_listing(caller, listing_id) |
Seller | Cancel an active listing |
update_listing_price(caller, listing_id, new_price) |
Seller | Update listing price |
buy_land(buyer, listing_id) |
Any | Purchase a listed parcel using XLM |
get_listing(listing_id) |
View | Fetch a listing by ID |
get_active_listings() |
View | List of all active listing IDs |
upgrade(caller, new_hash) |
Admin | Upgrade contract Wasm |
Non-fungible token contract representing land parcel ownership.
| Function | Access | Description |
|---|---|---|
initialize(admin, registry_contract) |
Once | Deploy and configure the NFT contract |
mint(to, token_id) |
Registry | Mint a locked NFT for a new parcel |
transfer(from, to, token_id) |
Registry / Owner | Transfer an unlocked token |
lock(token_id) |
Registry | Lock a token (prevents transfer) |
unlock(token_id) |
Registry | Unlock a token (after approval) |
set_base_uri(new_base_uri, updater) |
Admin | Update metadata base URI |
owner_of(token_id) |
View | Get the current token owner |
is_locked(token_id) |
View | Check if a token is locked |
base_uri() |
View | Get the current metadata base URI |
pub struct Land {
pub land_id: u64,
pub owner: Address,
pub location: Location,
pub area: u64, // square metres
pub land_use: String,
pub status: LandStatus,
pub last_transaction_timestamp: u64, // ledger timestamp
pub inspector: Address,
}
pub struct Location {
pub latitude: i64, // fixed-point × 1_000_000
pub longitude: i64,
}
pub enum LandStatus { Pending, Approved, Rejected }
pub struct Listing {
pub listing_id: u64,
pub land_id: u64,
pub seller: Address,
pub price: i128, // stroops (1 XLM = 10_000_000 stroops)
pub status: ListingStatus,
pub created_at: u64,
pub updated_at: u64,
}
pub enum ListingStatus { Active, Sold, Cancelled }| Tool | Version | Purpose |
|---|---|---|
| Rust | ≥ 1.78 | Contract compilation |
| Stellar CLI | ≥ 21.x | Deploy & interact with contracts |
| Node.js | ≥ 20 | Frontend & backend |
| pnpm | ≥ 10 | Monorepo package manager |
| PostgreSQL | ≥ 15 | Backend database |
# 1. Clone the repository
git clone https://github.com/NoshonNetworks/landbou.git
cd landbou
# 2. Install Node.js dependencies
pnpm installcd contracts/land_registry
# Add the Soroban WASM target (first time only)
rustup target add wasm32-unknown-unknown
# Build optimized contract WASM
cargo build --target wasm32-unknown-unknown --releasecd contracts/land_registry
cargo test# Configure the Stellar CLI (first time)
stellar keys generate --global alice --network testnet
# Deploy the LandNFT contract
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/land_nft.wasm \
--source alice \
--network testnet
# Deploy the LandRegistry contract
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/land_registry.wasm \
--source alice \
--network testnet
# Initialize the contracts (replace CONTRACT_IDs with deployed addresses)
stellar contract invoke \
--id <REGISTRY_CONTRACT_ID> \
--source alice \
--network testnet \
-- initialize \
--admin <ADMIN_ADDRESS> \
--nft_contract <NFT_CONTRACT_ID># From the repository root
pnpm run clientThe client starts on http://localhost:3000.
cd land-registry-backend
npm install
npm run dev# From the repository root
pnpm run landingThe landing page starts on http://localhost:3001.
cd indexers/land-registry
npm install
npm start┌─────────────────────────────────────────────────────────────────────┐
│ Landbou Flow │
│ │
│ 1. Connect Freighter (Stellar) wallet │
│ 2. Select role → Land Owner | Land Inspector │
│ │
│ Land Owner Dashboard │
│ ├── Register Land → provide GPS, area, land_use │
│ │ └── On-chain: LandRegistry::register_land │
│ │ └── LandNFT minted (locked, status = Pending) │
│ │ │
│ ├── My Collections → view registered parcels │
│ ├── Marketplace → browse, list, and purchase land │
│ │ ├── create_listing → list an approved parcel │
│ │ └── buy_land → purchase via XLM payment │
│ └── Notifications → real-time status updates │
│ │
│ Inspector Dashboard │
│ ├── Pending Queue → lands awaiting approval │
│ ├── Approve / Reject → approve_land | reject_land │
│ │ └── On approval: NFT unlocked, status = Approved │
│ ├── Transfer Oversight → verify ownership transfers │
│ └── Reports & Logs → transaction history per parcel │
│ │
│ Off-chain data (metadata, search) served by the backend API. │
└─────────────────────────────────────────────────────────────────────┘
pnpm run client # Start the frontend client (dev mode)
pnpm run landing # Start the landing page (dev mode)
pnpm run build # Build all packages for productionCreate a .env file in land-registry-backend/:
DATABASE_URL=postgresql://user:password@localhost:5432/landbou
JWT_SECRET=your_jwt_secret_here
PORT=4000
STELLAR_NETWORK=testnet
REGISTRY_CONTRACT_ID=CXXX...
NFT_CONTRACT_ID=CYYY...Create a .env.local file in app/client/:
NEXT_PUBLIC_STELLAR_NETWORK=testnet
NEXT_PUBLIC_REGISTRY_CONTRACT_ID=CXXX...
NEXT_PUBLIC_NFT_CONTRACT_ID=CYYY...This project uses ESLint and Husky pre-commit hooks to enforce code quality.
# Lint all JS/TS files
pnpm exec eslint --fix "**/*.{js,ts,tsx}"
# Rust formatting and linting
cd contracts/land_registry
cargo fmt
cargo clippyWe welcome contributions from the community. Please read the guidelines below before opening a pull request.
- Fork the repository and create your branch from
develop. - Follow the existing code style (ESLint for TypeScript,
rustfmtfor Rust). - Write tests for any new contract functions.
- Open a PR against the
developbranch with a clear description of your changes. - Ensure all CI checks pass before requesting a review.
See .github/PULL_REQUEST_TEMPLATE.md for the PR template.
| Document | Description |
|---|---|
| docs/getting_started.md | Detailed setup and usage guide |
| docs/land_registry.md | Land Registry contract reference |
| docs/land_nft.md | Land NFT contract reference |
| docs/api.md | Backend REST API reference |
| docs/Design.md | UI/UX design specifications |
This project is licensed under the MIT License. See LICENSE for details.
- X (Twitter): @Landbou
- GitHub: NoshonNetworks/landbou