This document describes the least-privilege deployment procedure for Teye contracts. The goal is to ensure the deployer key never retains permanent admin access after initialization completes.
Soroban contracts are initialized with an admin address that controls privileged operations (user management, provider verification, error log clearing). A compromised admin key puts the entire contract at risk.
The secure deployment workflow uses a temporary deployer key for the one-time deployment and initialization, then immediately transfers admin rights to a permanent admin key stored in a hardware wallet or multi-signature account.
Create the permanent admin keypair on a secure, air-gapped machine or hardware wallet. Record the public address only.
# Example using soroban CLI on an air-gapped machine
soroban keys generate permanent-admin
soroban keys address permanent-admin
# -> GBXXX... (the permanent admin public address)Store the secret key in a hardware wallet or encrypted vault. It should never exist on the deployment machine.
On the deployment machine, create a disposable keypair used only for this deployment session.
soroban keys generate deployer-tmp
soroban keys address deployer-tmpFund the deployer key with enough XLM to cover deployment fees.
Use the --admin flag to deploy, initialize, and transfer in one step:
./scripts/deploy.sh testnet vision_records --admin GBXXX...Or use the Makefile target:
ADMIN_ADDRESS=GBXXX... make deploy-secureThe script will:
- Build and deploy the WASM binary using the deployer key.
- Initialize the contract with the deployer as temporary admin.
- Call
transfer_adminto hand off admin rights to the permanent address. - Verify the transfer by querying
get_admin.
Confirm the admin was transferred:
soroban contract invoke \
--id <CONTRACT_ID> \
--network testnet \
-- \
get_adminThe output must show the permanent admin address, not the deployer.
After verifying the transfer, remove the temporary deployer key from the deployment machine:
soroban keys rm deployer-tmpThe transfer_admin contract function:
- Requires the current admin to authenticate the call.
- Updates the
ADMINstorage key to the new address. - Grants the
AdminRBAC role to the new address. - Downgrades the old admin to
Patientrole (no system permissions).
After transfer, the deployer key cannot perform any privileged operations.
If you run deploy.sh without --admin, the contract is deployed but not
initialized. You must initialize and transfer manually:
# Initialize with deployer as temp admin
soroban contract invoke \
--id <CONTRACT_ID> \
--source deployer-tmp \
--network testnet \
-- \
initialize --admin <DEPLOYER_ADDRESS>
# Transfer to permanent admin
soroban contract invoke \
--id <CONTRACT_ID> \
--source deployer-tmp \
--network testnet \
-- \
transfer_admin \
--current_admin <DEPLOYER_ADDRESS> \
--new_admin <PERMANENT_ADMIN_ADDRESS>Before deploying to mainnet:
- Permanent admin key generated on air-gapped hardware.
- Deployer key is a fresh, single-use keypair.
- Deployment uses
--adminflag or equivalent manual transfer. -
get_adminreturns the permanent admin address post-deployment. - Deployer key deleted from all machines after successful transfer.
- Deployment record saved in
deployments/withadmin_transferred: true.