xrpcs-semeion#
An ATProtocol XRPC service that signs payloads.
Overview#
Semeion is a lightweight microservice that provides cryptographic signing capabilities for the ATProtocol ecosystem. The service exposes a single XRPC endpoint that accepts arbitrary payloads and returns their cryptographic signatures.
The name "semeion" comes from the Greek word σημεῖον (sēmeîon) meaning "sign" or "mark", reflecting the service's core purpose of creating digital signatures.
Key features:
- Single-purpose XRPC signing endpoint
- OAuth-based authentication and authorization
- DID:web identity publication
- LRU caching for DID document resolution
- Configurable via environment variables
Administrator's Guide#
Generating Signing Keys#
Before configuring the service, you'll need to generate cryptographic keys:
# Generate a new P-256 signing key
goat key generate -t p256
This command will output a private key that you'll use for the SERVICE_KEY or SIGNER_KEY configuration.
Important: The public key derived from your signing key must be included in your DID document. This allows other services to verify signatures created by semeion.
For more information on ATProtocol key management and DID documents, see: https://whtwnd.com/bnewbold.net/3lj7jmt2ct72r
Configuration#
The service requires the following environment variables:
| Variable | Required | Description |
|---|---|---|
SERVICE_KEY |
Yes | Private key for the service's identity (e.g., did:key:z42tj..) |
EXTERNAL_BASE |
Yes | External hostname where the service is accessible (e.g., semeion.example.com) |
SIGNER_DID |
Yes | DID of the authorized signer (e.g., did:plc:abc123..) |
SIGNER_KEY |
Yes | Private key used for signing operations (e.g., did:key:z42to..) |
PORT |
No | HTTP server port (default: 8080) |
PLC_HOSTNAME |
No | PLC directory hostname (default: plc.directory) |
USER_AGENT |
No | HTTP User-Agent header for external requests |
DNS_NAMESERVERS |
No | Comma-separated list of DNS nameservers |
CERTIFICATE_BUNDLES |
No | Comma-separated list of additional CA certificate bundle paths |
Building and Running#
Local Development#
# Clone the repository
git clone https://tangled.sh/@smokesignal.events/xrpcs-semeion
cd xrpcs-semeion
# Build the project
cargo build --release
# Run with environment variables
SERVICE_KEY="did:key:your-service-key" \
EXTERNAL_BASE="semeion.example.com" \
SIGNER_DID="did:plc:abc123" \
SIGNER_KEY="did:key:your-signer-key" \
cargo run --release
Docker Container#
The project includes a multi-stage Dockerfile that builds a minimal container using distroless base image.
# Build the container
docker build --pull -t xrpcs-semeion .
# Run the container
docker run -d \
-p 8080:8080 \
-e SERVICE_KEY="did:key:your-service-key" \
-e EXTERNAL_BASE="semeion.example.com" \
-e SIGNER_DID="did:plc:abc123" \
-e SIGNER_KEY="did:key:your-signer-key" \
xrpcs-semeion
Deployment Considerations#
-
TLS Termination: The service runs HTTP internally. Use a reverse proxy (nginx, Caddy, etc.) for TLS termination.
-
Health Checks: The root endpoint (
/) can be used for health checks. -
Monitoring: The service logs important events using the
tracingcrate. Configure log levels via theRUST_LOGenvironment variable. -
Key Security: Store private keys securely. Consider using environment variable secrets management in production.
Integrator's Guide#
XRPC Method Definition#
The service provides a single XRPC method: garden.lexicon.ngerakines.semeion.Sign
{
"lexicon": 1,
"id": "garden.lexicon.ngerakines.semeion.Sign",
"defs": {
"main": {
"type": "procedure",
"description": "Sign a payload",
"input": {
"encoding": "*/*"
},
"output": {
"encoding": "application/octet-stream"
}
}
}
}
It can be resolved and validated with goat lex resolve garden.lexicon.ngerakines.semeion.Sign.
Authentication#
The service uses OAuth 2.0 bearer token authentication. Your token must include:
lxmclaim: Must matchgarden.lexicon.ngerakines.semeion.Signissclaim: Must match the configuredSIGNER_DID
Making Requests#
Example using curl#
# Sign a payload
curl -X POST https://semeion.example.com/xrpc/garden.lexicon.ngerakines.semeion.Sign \
-H "Authorization: Bearer YOUR_OAUTH_TOKEN" \
--data-binary "@the_file"
Error Responses#
The service returns standard HTTP status codes:
200 OK: Successful signature creation400 Bad Request: Invalid request format or empty body401 Unauthorized: Missing or invalid authentication500 Internal Server Error: Signing operation failed
Error responses include a JSON body:
{
"error": "error_code",
"error_description": "Human-readable error description"
}
Service Discovery#
The service publishes its identity via:
/.well-known/did.json- DID:web document/.well-known/atproto-did- ATProtocol DID identifier/.well-known/oauth-protected-resource- OAuth Protected Resource Metadata
License#
This project is open source under the MIT License.
MIT License
Copyright (c) 2024 Nick Gerakines
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.