This repository provides a production-ready content delivery solution for OpenSimulator 9.3 consisting of:
UuidPasswordContentDeliveryModule– a C#ISharedRegionModulethat authenticates avatar UUID/password pairs, proxies asset delivery, and integrates with the bundled PHP web service.- PHP 8.2 web service – exposes
/api/auth,/api/authorize, and/api/downloadendpoints with strict validation, Argon2id password hashing, signed URLs, and rate limiting. - Infrastructure assets – migrations, configuration samples, automated tests, and documentation.
opensim/ # OpenSimulator module sources
PhpApiClient.cs # Internal HTTP client with retries/backoff
UuidPasswordContentDeliveryModule.cs
php-app/ # PHP backend application
public/index.php # Entry point/router
src/ # PHP services (Auth, Authorize, Validator, etc.)
tests/ # PHPUnit test suite
migrations/001_init.sql # MySQL schema and seed scaffold
README.md # This document
marketplace-for-api.php # WordPress marketplace plugin variant using the delivery API
The marketplace-for-api.php plugin replaces the legacy Corrade integration with calls to the new delivery API. Key features include:
- Manual and scheduled prim imports with per-region offsets and ignore rules.
- Order handling that sends a GET request to the configured delivery endpoint (
oid,uid,pass, and optionalregion). - Support for multiple store regions: provide a list of region UUIDs in Settings → OpenSim Marketplace → Region Synchronisation and the importer will process each region in turn.
- Friendly region labels: enter entries as
region-uuid|Display Name(one per line) to show user-friendly names in filters, cards, and admin tables. - Manual sync checkboxes let you trigger a sync for selected regions directly from the admin page; leave unchecked to process every configured region.
- Copy
marketplace-for-api.phpto your WordPresswp-content/plugins/directory and activate it from the Plugins screen. - Visit Settings → OpenSim Marketplace and configure:
- Delivery API URL and password (shared secret).
- Region UUID list (newline separated, optional
|Friendly Name). - Database credentials for OpenSim and money servers.
- Optionally schedule cron via WordPress or a system cron job to trigger the five-minute import (
wp cron event run osmp_cron_import).
The storefront shortcode remains [osmp_market]; visitors can filter by friendly region names once the configuration is saved.
-
Copy
opensim/UuidPasswordContentDeliveryModule.csandopensim/PhpApiClient.csinto your OpenSim source tree (e.g.OpenSim/Region/OptionalModules/). -
Add the module name to your
OpenSim.ini:[UuidPasswordContentDelivery] Enabled = true PhpApiBaseUrl = "https://content.example.com" SharedSecret = "CHANGE_ME" RequestTimeoutMs = 5000 MaxRetries = 2 ProxyBinary = false RateLimitBackoffMs = 500 LoggingLevel = "INFO"
-
Rebuild OpenSim if required and restart the simulator. The module automatically registers
/content-delivery/auth-check(health) and/content-delivery/fetch(content) handlers on each region.
Note: The module targets .NET 6.0 as required by OpenSim 9.3 and respects multi-region deployments.
-
Install dependencies:
cd php-app composer install --no-dev --optimize-autoloader -
Copy
.env.exampleto.envand update the values:cp .env.example .env
Key Description APP_BASE_URLPublic HTTPS base URL (e.g. https://content.example.com).DB_*MySQL DSN/credentials. HMAC_SECRETSecret used to sign /api/downloadURLs.SHARED_SECRETShared module secret (must match OpenSim SharedSecret).SIGNED_URL_TTL_SECONDSSigned URL lifetime (default 600s). TOKEN_TTL_SECONDSAuthentication token lifetime (default 600s). RATE_LIMIT_PER_MINUTERequests per minute per client. DOWNLOAD_STRATEGYsigned_url(default) orinline.STORAGE_BASE_PATHAbsolute path to on-disk asset store. -
Apply the migration to your MySQL instance:
mysql -u opensim -p opensim < migrations/001_init.sql -
Configure your web server (nginx/Apache) to route HTTPS requests to
php-app/public/index.phpvia PHP-FPM. -
Ensure TLS termination is active; the service rejects non-HTTPS requests when
FORCE_HTTPS=true.
- Authenticate – The module posts
{avatar_uuid, password}to/api/auth. The PHP service validates credentials (Argon2id hashed passwords) and returns{ok, token, expires_at}. - Authorize – The module posts
{token, request, id}to/api/authorizeto request an asset, inventory listing, or package. A successful response contains eitherbinarycontent (base64) or a signed download URL. - Download – When a signed URL is returned, the module either redirects the viewer or proxies the binary (configurable) by calling
/api/download?id=...&expires=...&sig=....
curl -sS https://content.example.com/api/auth \
-H 'Content-Type: application/json' \
-d '{"avatar_uuid":"123e4567-e89b-12d3-a456-426614174000","password":"CorrectHorseBatteryStaple"}'
curl -sS https://content.example.com/api/authorize \
-H 'Content-Type: application/json' \
-d '{"token":"<token>","request":"asset","id":"123e4567-e89b-12d3-a456-426614174001"}'
curl -L "https://content.example.com/api/download?id=123e4567-e89b-12d3-a456-426614174001&expires=<ts>&sig=<sig>"Run the automated suites from the repository root:
# PHP unit tests
(cd php-app && composer install && ./vendor/bin/phpunit)
# .NET unit tests for PhpApiClient
(dotnet test tests/cs/UuidPasswordContentDeliveryModule.Tests.csproj)Both suites provide coverage for request validation, token issuance, signed URL generation, and HTTP client retry logic.
- UUIDs are validated client- and server-side.
- Passwords are hashed with Argon2id; tokens are stored hashed and expire automatically.
- Signed URLs use HMAC-SHA256 with configurable TTL.
- PHP endpoints enforce per-client rate limiting and reject non-HTTPS requests in production.
- The OpenSim module performs exponential backoff and structured logging with correlation IDs.
- Update dependencies via
composer updateanddotnet restoreas needed. - Rotate
HMAC_SECRETperiodically and sync with OpenSim configuration. - Purge expired tokens with a scheduled job (
DELETE FROM auth_tokens WHERE expires_at < NOW()).