**This is an old revision of the document!**
User QR Encoding
This document formally defines a QR Code format that encodes the minimum identifiable user information for use by other services.
Use Cases
- Recommended, i.e. good, use cases:
- Verifying usage of non-critical machinery per user
- Tally-counting access/usage of certain things
- Non-critical user-personalized actions, such as printing out news for a user on a receipt printer
- NOT recommended, i.e. possible but please don't:
- Door Badging (NOT recommended, see Security)
- Access-gating critical/dangerous machinery
Requirements
- Encodes at minimum the user ID and username
- Robust at allowing users to change username
- Allow insecure/non-critical systems to obtain embedded information without computation/verification if needed for low-power/embedded purposes
- Allow secure verification using remote server to obtain additional, more secretive information about current user (e.g., list of groups or avatar URL)
- QR Code must be small (has at most 4 eyes in most cases, rather than 9 eyes or more)
QR Code Design
The QR Code encodes a string encoded in Alphanumeric mode. The string must implement this syntax:
QR_STRING := 'HTTPS://DMA.SPACE/QR/' CLAIMS_PART '.' SIGNATURE_PART
Note: All characters MUST be upper-cased for Alphanumeric mode.
Base32 Encoding
The QR string makes extensive use of standard Base32 encoding with NO padding, as defined in RFC 4648.
Claims Part
CLAIMS_PART := USER_ID ':' USERNAME_BASE32 ':' USER_ROLE ':' ISSUED_DATE USER_ROLE := 'ADMIN' | 'MEMBER' | '_' ISSUED_DATE := YEAR '-' MONTH '-' DATE
The claims part is described in the syntax above, with the following notes:
UNAME_BASE32means the username is encoded using the predefined Base32 encoding.- If the user's role has neither admin nor member, then
_must be used to denote either normal user or unknown role.
As an example, this claims string:
10:MRUWC3LPNZSA:ADMIN:2026-01-01
Means the following:
- The user's ID is
10 - The username is
diamond(decoded from the Base32 stringMRUWC3LPNZSA) - The user's role is
admin, meaning they belong in thedma-adminsIdP group - The claims dates back to January 1st, 2026
Signature Type and Part
SIGNATURE_PART := SIGNATURE_ED25519 | ... SIGNATURE_ED25519 := 'ED25519:' BASE32_DATA
The signature type denotes the type of the signature string that follows after it and the colon (:) character.
The signature part is the signature of the entire Claims Part string, signed using whatever the signature type denotes, and encoded using the predefined Base32 encoding.
The supported signature types are:
ED25519
An example signature part of the above claims part string example could be:
ED25519:7CSS7U7C2BJM3Z3MXYENYNSBUWZRS3BGT4YWX4DXTMDBOWUABFBT4REZSKJ4FCVTFXCFY6A2WNOUIMIR3HHGLQT5CNA5ZABNOBPBMBY
Implementation and Example
The following function implements the algorithm in Go using a fake random source to generate the Ed25519 key (INSECURE):
func encodeEd25519(priv ed25519.PrivateKey, uid int, uname string, urole string, issuedAt time.Time) []byte { enc := base32.StdEncoding.WithPadding(base32.NoPadding) plx := fmt.Sprintf( "%d:%s:%s:%s", uid, enc.EncodeToString([]byte(uname)), urole, issuedAt.Format("2006-01-02"), ) sig := ed25519.Sign(priv, []byte(plx)) return fmt.Appendf(nil, "HTTPS://DMA.SPACE/QR/%s.ED25519:%s", plx, enc.EncodeToString(sig)) }
The example outputs above was generated with:
issuedAt := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)
constRand := rand.NewChaCha8([32]byte{})
_, priv, _ := ed25519.GenerateKey(constRand)
encodeEd25519(priv, 10, "diamond", roleAdmin, issuedAt)
This returns the following string:
HTTPS://DMA.SPACE/QR/10:MRUWC3LPNZSA:ADMIN:2026-01-01.ED25519:7CSS7U7C2BJM3Z3MXYENYNSBUWZRS3BGT4YWX4DXTMDBOWUABFBT4REZSKJ4FCVTFXCFY6A2WNOUIMIR3HHGLQT5CNA5ZABNOBPBMBY
Frontend Server Design
The frontend server must be able to handle /QR paths and perform a redirection to the Monolith API server's GET /api/qr endpoint.
Verification Server Design
This section is not finalized.
curl -X GET "https://dma.space/QR/LMYTAMBMEJWWK33XNVSW653NMVXXO3LFN53W2ZLPO4RCYITNMVWWEZLSEJOQ.ED25519:V2OZNQJDFNNUJ26XOFY5SEKIG73RNGJJ7AI7YDTHD77V7VGTSXQA4MYH2KZIJT6RB7WAFZUU5DCMFEAB2KL5HZQG27HD7AZZGLPUAAI"
{
"valid": true,
"qr_claims": [10, "diamond", "admin"],
"auth_claims": {
"sub": 10,
"username": "diamond",
"preferred_name": "diamond",
"email": "[email protected]",
"groups": ["dma-users", "dma-members", "dma-admins"]
}
}
The Monolith should implement a GET /api/qr.
Security
This design does prevent fake QR codes from being created, so imitating another person is not trivial.
However, the QR code can still be copied or stolen from others, so the whole system is not perfect and should not be used to gate access to critical things.