Skip to content

Instantly share code, notes, and snippets.

@fabiolimace
Last active January 3, 2026 05:57
Show Gist options
  • Select an option

  • Save fabiolimace/8821bb4635106122898a595e76102d3a to your computer and use it in GitHub Desktop.

Select an option

Save fabiolimace/8821bb4635106122898a595e76102d3a to your computer and use it in GitHub Desktop.
SHA-256 UUIDv8 and Tag URI

SHA-256 UUIDv8 and Tag URI

This document defines two unique identifiers:

  • a custom UUID based on RFC 9562;
  • a custom Tag URI based on RFC 4151.

SHA-256 UUIDv8

UUIDv8 is a UUID type defined by RFC 9562 that allows for customization.

Our custom UUID is generated by UUIDv8.generate(), which uses a SHA-256 hash algorithm.

SHA-256 was chosen because it is natively available on Java 7+ and PostgreSQL 11+.

Additionally, two bits are left unset to make them reserved, also to make it easier to implement our UUID in other languages.

This example is a util class that generates UUIDv8 based on SHA-256:

public class UUIDv8 {
    
    /**
     * Generate a SHA-256-based UUIDv8.
     * <p>
     * No namespace is required like in UUIDv5. Also 2 bits are reserved unset so that
     * the generation is a lot easier in other languages by using substring functions.
     * <p>
     * 
     * @param a string
     * @return a UUID
     */
    public static UUID generate(String str) {
        
        final String algoritmo = "SHA-256";
        final MessageDigest digest = digest(algoritmo);
        
        // Calculate hash from which 16 bytes will be used
        digest.update(str.getBytes(StandardCharsets.UTF_8));
        ByteBuffer hash = ByteBuffer.wrap(digest.digest());
        
        // Format the 16 bytes from the hash according to RFC 9562 and reserve 2 bits
        hash.put(6, (byte) (hash.get(6) & 0x0f | 0x80)); // inject the 4 version bits
        hash.put(8, (byte) (hash.get(8) & 0x3f | 0x80)); // inject the 2 variant bits
        hash.put(8, (byte) (hash.get(8) & 0b1100_1111)); // unset 2 bits to reserve them
        
        return new UUID(hash.getLong(), hash.getLong());
    }
    
    private static MessageDigest digest(String algorithm) {
        try {
            return MessageDigest.getInstance(algorithm);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException(e.getMessage());
        }
    }
}
UUIDv8.generate("tag:example.com,2024:application:instance:entity:123");
745e831a-9f04-8dda-884a-5d62bce8fc9c

💡 HINT
The fact that the our method doesn't expect a namespace (like UUIDv5 does) doesn't mean you can't use one. For example, you can put a private prefix in your input string, just like a salt.

UUIDv8.generate("MY_PRIVATE_PREFIX" + "tag:example.com,2024:application:instance:entity:123");
22f6a9b4-637a-81ce-81f6-6efc8b74c6f8

This example shows how to generate this UUIDv8 on Bash:

echo -n "tag:example.com,2024:application:instance:entity:123" | sha256sum \
  | awk '{ print substr($0,1,12) "8" substr($0,14,3) "8" substr($0,18,15) }'
745e831a9f048dda884a5d62bce8fc9c

This is another example but now with PostgreSQL's SQL:

select overlay (
       overlay (
           substring(
               encode(
                   sha256('tag:example.com,2024:application:instance:entity:123')
               , 'hex')
           , 1, 32)
       placing '8' from 13 for 1)
       placing '8' from 17 for 1);
745e831a9f048dda884a5d62bce8fc9c

Tag URI

RFC 4151 specifies the 'tag' URI scheme.

A Tag URI is a kind of unique ID. It looks like a URL, but it doesn't say how to find (locate) a resource; it only identifies the resource.

💡 HINT
Read https://taguri.org/

We are using this Tag URI format (without spaces):

tag : example.com , 2024 : application : instance : entity : 123

where:
* `tag` is the scheme name, which is fixed;
* `example.com` is our domain name;
* `2024` is an year we own the domain name;
* `application` is the application name;
* `instance` is the application instance name;
* `entity` is a persistence entity name;
* `123` is the ID of a persistence entity.

The entity below shows how to use our UUIDv8 with Tag URI in a JPA Entity:

@Entity
public class MyEntity {
    
    @Id
    @Column(name = "id")
    private UUID id;
    
    @Column(name = "tag")
    @Pattern(regexp = "^tag:\\w+,\\w+:\\w+:\\w+:\\w+:\\w+$")
    private String tag;
    
    public MyEntity(String tag) {
        this.tag = tag;
    }
    
    @PrePersist
    public void prePersist() {
        if (id == null) {
            id = UUIDv8.generate(tag);
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment