lets.dj

A name and a certificate

The Stage is an HTTPS page, and a browser will only load media from an address whose certificate it already trusts. A provider on a home network has neither: nobody can get a public certificate for 192.168.1.20. So there are two things we host, for any provider that asks.

You do not have to use them. A provider may serve from any name, with any certificate a browser trusts, and nothing else in this specification depends on these two routes. They are for the case where a name and a certificate would otherwise be the hardest part of writing a provider.

A name

Every library has a name of its own on our domain: <bridgeId>.letsdj.io, for instance 0123456789abcdef.letsdj.io. You are told it when you pair, and again by each of the routes below. Use hostname as given and do not build it yourself.

The name is an A record, and you tell us what it should point at:

POST https://api.lets.dj/providers/address
Authorization: Bearer <sync token>
Content-Type: application/json

{ "address": "192.168.1.20" }
{
  "ok": true,
  "hostname": "0123456789abcdef.letsdj.io",
  "address": "192.168.1.20",
  "ttl": 60,
  "changed": false
}

Report the address when you start, and again whenever it changes, which is what keeps the name pointing at the right place after a machine moves from home wifi to a hotspot. changed says whether the record had to be written or already held that value. The record is published with a time to live of 60 seconds.

The address must be IPv4, in dotted-quad form, and must be one that cannot be reached from the internet: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, link-local (169.254.0.0/16), or carrier-grade NAT (100.64.0.0/10). Anything else is refused. We are about to write what you send into a public DNS zone, and left unchecked that would be a free authoritative resolver for anything, pointed anywhere. A name that can only ever resolve to somebody’s own network is also what makes it safe for the zone to be readable by everyone. Octets with a leading zero are refused as well, because 010.0.0.1 means different things to different parsers.

  • 400 invalid_address: not an IPv4 address in dotted-quad form.
  • 400 address_not_private: a valid address, but not in one of the ranges above.
  • 403 unknown_sync_token, and 401 missing_token.
  • 502 dns_unavailable: the record could not be written just now. Try again shortly.
  • 503 dns_not_configured: this deployment cannot publish names at all.

A name that has not been heard from in thirty days is taken down. Reporting an address, or fetching the certificate below, counts as being heard from. A provider that is running fetches its certificate several times a day, and is never that quiet. One that comes back after a long silence simply has its record made again.

A known limitation. Some routers and DNS filtering services refuse to answer when a public name points at a private address. It is called DNS rebinding protection, and when it is on, the name does not resolve for anyone behind it and the Stage cannot reach you. There is no fix from our side. Many routers that have the setting let you exempt a domain, and letsdj.io is the one to exempt.

A certificate

GET https://api.lets.dj/providers/certificate
Authorization: Bearer <sync token>
{
  "hostname": "0123456789abcdef.letsdj.io",
  "cert": "-----BEGIN CERTIFICATE-----\n…",
  "key": "-----BEGIN PRIVATE KEY-----\n…",
  "notAfter": 1798000000000
}

cert and key are PEM. notAfter is when the certificate expires, in milliseconds since the epoch. The certificate is for *.letsdj.io, so it covers your hostname on any port, and every provider receives the same certificate and key, together with its own hostname. The response is marked Cache-Control: no-store.

Ask for it when you start, and then again a few times a day. Swap a new one in when you are given one, without restarting, so that renewals and any forced reissue reach a running provider. Keep the last good certificate on disk, so that a provider which starts with no network can still serve until it expires.

  • 503 certificate_unavailable with Retry-After: 300: none has been issued yet. Try again in a few minutes.
  • 503 secrets_unavailable: the service cannot read its stored secrets just now.
  • 403 unknown_sync_token, and 401 missing_token.

What we will not do

We do not ship, bundle, pre-install, endorse, link to, or recommend any third-party provider. That is not squeamishness — Grokster treats the absence of safeguards as evidence of intent, and Filmspeler turns on pre-installation and promotion. The distance is what makes the neutral-tool position real rather than decorative, and it only works if it is actually maintained.

We do host two pieces of infrastructure, on the same terms for every provider. A provider holding a sync token may ask us to publish a name for its address (POST /providers/address, refused unless the address is private, link-local or carrier-grade NAT), and may fetch the shared *.letsdj.io certificate that lets a browser trust that name (GET /providers/certificate). Both authenticate a sync token and read nothing else.

Neither touches the content. Media is fetched by the Stage straight from the provider and never passes through us, and a catalogue is the metadata a provider chose to send, which we neither review nor curate. A name and a certificate are what any web service needs in order to be reachable by a browser.