An ATProtocol XRPC service that signs payloads.
Rust 91.8%
Dockerfile 8.2%
3 1 0

Clone this repository

https://tangled.org/smokesignal.events/xrpcs-semeion https://tangled.org/did:plc:tgudj2fjm77pzkuawquqhsxm/xrpcs-semeion
git@knot.tangled.wizardry.systems:smokesignal.events/xrpcs-semeion git@knot.tangled.wizardry.systems:did:plc:tgudj2fjm77pzkuawquqhsxm/xrpcs-semeion

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

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#

  1. TLS Termination: The service runs HTTP internally. Use a reverse proxy (nginx, Caddy, etc.) for TLS termination.

  2. Health Checks: The root endpoint (/) can be used for health checks.

  3. Monitoring: The service logs important events using the tracing crate. Configure log levels via the RUST_LOG environment variable.

  4. 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:

  • lxm claim: Must match garden.lexicon.ngerakines.semeion.Sign
  • iss claim: Must match the configured SIGNER_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 creation
  • 400 Bad Request: Invalid request format or empty body
  • 401 Unauthorized: Missing or invalid authentication
  • 500 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.