Production-ready PHP server for remote signing of SEP-10 and SEP-45 client domain authentication requests.
This server implements remote signing capabilities for Stellar authentication protocols:
- SEP-10: Web Authentication - Signs transaction envelopes for user authentication
- SEP-45: Web Authentication for Soroban Contracts - Signs authorization entries for contract-based authentication
- SEP-10 transaction signing endpoint
- SEP-45 authorization entries signing endpoint
- Stellar TOML serving
- Bearer token authentication
- CORS support
- Health check endpoint
- Configuration via JSON file or environment variables
- PHP 8.1 or higher
- Composer
- Stellar PHP SDK 1.8.0 or higher
Clone the repository and install dependencies:
git clone <repository-url>
cd php-server-signer
composer installThe server can be configured using either a JSON configuration file or environment variables.
Create a config.json file (see config.example.json):
{
"host": "0.0.0.0",
"port": 5003,
"account_id": "GBUTDNISXHXBMZE5I4U5INJTY376S5EW2AF4SQA2SWBXUXJY3OIZQHMV",
"secret": "SBRSOOURG2E24VGDR6NKZJMBOSOHVT6GV7EECUR3ZBE7LGSSVYN5VMOG",
"network_passphrase": "Test SDF Network ; September 2015",
"soroban_rpc_url": "https://soroban-testnet.stellar.org",
"bearer_token": "987654321"
}Alternatively, set these environment variables (see .env.example):
export HOST=0.0.0.0
export PORT=5003
export ACCOUNT_ID=GBUTDNISXHXBMZE5I4U5INJTY376S5EW2AF4SQA2SWBXUXJY3OIZQHMV
export SECRET=SBRSOOURG2E24VGDR6NKZJMBOSOHVT6GV7EECUR3ZBE7LGSSVYN5VMOG
export NETWORK_PASSPHRASE="Test SDF Network ; September 2015"
export SOROBAN_RPC_URL="https://soroban-testnet.stellar.org"
export BEARER_TOKEN=987654321php public/index.php -c config.jsonphp public/index.phpThe server uses PHP's built-in web server for development. For production deployments, use a proper web server like nginx or Apache with PHP-FPM.
Health check endpoint.
Authentication: Not required
Response:
{
"status": "ok"
}Example:
curl http://localhost:5003/healthReturns the Stellar TOML file with the signing key.
Authentication: Not required
Response:
ACCOUNTS = ["GBUTDNISXHXBMZE5I4U5INJTY376S5EW2AF4SQA2SWBXUXJY3OIZQHMV"]
SIGNING_KEY = "GBUTDNISXHXBMZE5I4U5INJTY376S5EW2AF4SQA2SWBXUXJY3OIZQHMV"
NETWORK_PASSPHRASE = "Test SDF Network ; September 2015"Example:
curl http://localhost:5003/.well-known/stellar.tomlSigns a SEP-10 transaction envelope.
Authentication: Required (Bearer token)
Request:
{
"transaction": "<base64 XDR envelope>",
"network_passphrase": "Test SDF Network ; September 2015"
}Response:
{
"transaction": "<signed base64 XDR envelope>",
"network_passphrase": "Test SDF Network ; September 2015"
}Example:
curl -X POST http://localhost:5003/sign-sep-10 \
-H "Authorization: Bearer 987654321" \
-H "Content-Type: application/json" \
-d '{
"transaction": "AAAAAgAAAAD...",
"network_passphrase": "Test SDF Network ; September 2015"
}'Signs a SEP-45 authorization entry for client domain verification.
Authentication: Required (Bearer token)
Request:
{
"authorization_entry": "<base64 XDR of single SorobanAuthorizationEntry>",
"network_passphrase": "Test SDF Network ; September 2015"
}Response:
{
"authorization_entry": "<signed base64 XDR of entry>",
"network_passphrase": "Test SDF Network ; September 2015"
}Errors:
{
"error": "entry address does not match signing key"
}Example:
curl -X POST http://localhost:5003/sign-sep-45 \
-H "Authorization: Bearer 987654321" \
-H "Content-Type: application/json" \
-d '{
"authorization_entry": "AAAAAgAAAAD...",
"network_passphrase": "Test SDF Network ; September 2015"
}'- Store secrets securely (use environment variables or secure secret management)
- Use HTTPS in production
- Implement rate limiting
- Rotate bearer tokens regularly
- Use strong, randomly generated bearer tokens
- Monitor and log authentication failures
- Consider implementing IP whitelisting
- Never commit
config.jsonor.envfiles with real secrets
Run the unit tests:
composer testOr using PHPUnit directly:
vendor/bin/phpunitRun tests with coverage:
vendor/bin/phpunit --coverage-html coverage.
├── public/
│ └── index.php # Main application entry point
├── src/
│ ├── Config/
│ │ └── Config.php # Configuration management
│ ├── Handler/
│ │ └── Router.php # HTTP routing and handlers
│ └── Signer/
│ ├── Sep10Signer.php # SEP-10 signing logic
│ └── Sep45Signer.php # SEP-45 signing logic
├── tests/
│ ├── Config/
│ │ └── ConfigTest.php
│ ├── Handler/
│ │ └── RouterTest.php
│ └── Signer/
│ ├── Sep10SignerTest.php
│ └── Sep45SignerTest.php
├── .env.example
├── .gitignore
├── composer.json
├── config.example.json
├── phpunit.xml
└── README.md
All endpoints return appropriate HTTP status codes:
200 OK- Successful operation400 Bad Request- Invalid request parameters or malformed data401 Unauthorized- Missing or invalid authentication405 Method Not Allowed- Wrong HTTP method used500 Internal Server Error- Server error
Error responses include a JSON body with error details:
{
"error": "error message"
}The SEP-45 signing process involves:
- Decoding the base64 XDR of a single
SorobanAuthorizationEntry - Validating that the entry uses address credentials and that the address matches the signing key
- Fetching the current ledger from Soroban RPC and setting signature expiration ledger
- Building a
HashIdPreimagewith typeENVELOPE_TYPE_SOROBAN_AUTHORIZATIONcontaining:network_id(SHA256 hash of network passphrase)noncefrom address credentialssignature_expiration_ledgerfrom address credentialsroot_invocationfrom the entry
- Computing SHA256 hash of the preimage
- Signing the hash with the keypair
- Setting the signature as an
SCValVec containing a Map withpublic_keyandsignaturebytes - Returning the signed entry as base64 XDR
For production deployment:
- Use a production-grade web server (nginx, Apache) with PHP-FPM
- Configure reverse proxy with HTTPS
- Set up monitoring and logging
- Implement rate limiting at the reverse proxy level
- Use secure secret management (environment variables, HashiCorp Vault, AWS Secrets Manager, etc.)
- Enable PHP OPcache for better performance
- Set appropriate PHP memory limits and timeout values
- Use a process manager (systemd, supervisor) to ensure server availability
See PROMPT_DEPLOY_SERVER.md for detailed deployment instructions.
Build and run with Docker:
docker build -t stellar-php-server-signer .
docker run -p 5003:5003 \
-e ACCOUNT_ID=GBUTDNISXHXBMZE5I4U5INJTY376S5EW2AF4SQA2SWBXUXJY3OIZQHMV \
-e SECRET=SBRSOOURG2E24VGDR6NKZJMBOSOHVT6GV7EECUR3ZBE7LGSSVYN5VMOG \
-e BEARER_TOKEN=987654321 \
stellar-php-server-signerOr use Docker Compose:
docker-compose upApache 2.0