This shouldn't really matter as your import paths are obviously different. `github.com/google/uuid` and `github.com/sdrapkin/guid` can happily coexist. Any file/codebase importing both (which would ideally be avoided in the first place) can alias them.
> IMHO "Guid" is just as well known
I think the point the commenter was trying to make is that these do not adhere to the UUID spec. You don't specify which version, but judging by the docs and your comparison to `github.com/google/uuid`, I'd wager most folks looking at this library would assume they are supposed to be V4 UUIDs.
I think the point is that this just generates 16 random bytes whereas UUIDs/GUIDs have structure, they at least have a variant fields indicating what kind of UUID/GUID it is. The closest thing to all random bytes would be variant 10xx, version 4 or 8.
Advertising any UUID/GUID generator as cryptographically secure, or relying on it to be so, is a mistake, in my opinion.
You use a UUID when you need a universally unique ID whose guessability properties are not a critical security requirement. While the V4 UUID spec (which this package does not implement, but most users might assume it does) states that a UUID implementation SHOULD be cryptographically secure [1], it also states that they MUST NOT be used as security capabilities [2]. This is b/c they are not intended as secure tokens, but many users mistakenly assume them to be suitable as such. Not to mention, V4 UUIDs only have 122 bits of entropy, not 128, since 6 bits are reserved for version and variant information, which many users don't realize.
So you can generate a UUID that is suitable as a secure token, but at that point don't call it a UUID. Just call it a secure token. And if you need a secure token, use something like Go's `Text()` function from `crypto/rand` [3].
The situation reminds me of how the Go team updated the `math/rand` and `math/rand/v2` packages to use a CSPRNG as a defensive measure [4], while still urging users to use `crypto/rand` in secure contexts.
The vast majority of Golang developers would benefit from using Guid library instead of UUID library. It’s substantially faster in all cases, more secure (by 2^6) and has more functionality.
For random token-as-string generation Golang developers should be using https://github.com/sdrapkin/randstring instead of crypto/rand.Text (faster and more flexible).
The vast majority of Golang developers are neither hobbled by the lack of gigabyte throughput for random identifier generation nor are they on the verge of becoming victims to attacks on identifiers with "only" 2^122 random bits.
- This uses global state under the hood. Surprise! Is it thread safe? I’m not a Go expert, but it looks non-thread-safe.
- The copying code reminds me of old-school awful C buffer handling code. Maybe it’s right. Maybe it’s wrong. But it’s not obviously right.
- The actual meat is a cryptographic randomness cache. This is a subtle thing, and all the best practices are missing. Where’s the backtracking protection? What if the program forks? vDSO getrandom() knows how to do this correctly — something high-level should use it, not reimplement it incorrectly.
Global use looks fine - it's a very-simply-used sync pool to do larger blocks of rand reads, which makes plenty of sense for performance.
Unsafe use also looks fine, values either don't escape the function (a type string->byte type cast for function signature reasons) or they do but they're new temporary data (the byte->string cast, which is fine because there's no risk of reusing or modifying the original bytes).
I'm going to intentionally not make any claims to "cryptographic security" or "is this a GUID" as I'm not super clear on the details there. The code looks pretty normal to me though, with the possible exception of the base64 encoding (why not base64.URLEncoding? https://pkg.go.dev/encoding/base64#pkg-variables).
Looks safe to me. It uses `crypto/rand.Read` which is declared as safe for concurrent use. The cache is accessed via sync.Pool which is thread safe. As a check, I ran the tests with `-race` and it passed.
Thanks for your feedback. If you are skilled in Golang, I suggest you review the code more thoroughly for a more accurate understanding (especially compared to what standard uuid does).
My understanding was that speed is not something you want in a UUID generator, since it makes it more susceptible to brute force attacks. Is this not the case?
I've been using Cuid2[1] in most of my personal projects (this Go implementation[2], actually), which is fast enough, but not "too fast". It's also secure, collision resistant, and has everything I would need from a UUID.
> My understanding was that speed is not something you want in a UUID generator, since it makes it more susceptible to brute force attacks. Is this not the case?
The only possible think I can think of here is using a UUID version with a small space for the random bits, such that you could accidentally collide by generating them too fast. But with something like UUIDv7, you'd need to be generating hundreds of millions of random UUIDs every nanosecond in order for that to be a realistic concern.
cuid2 generates variable-length strings. If you want fast cryptographically strong string generation, I recommend https://github.com/sdrapkin/randstring. It will likely be faster than cuid2.
Amazon AWS S3 web servers process millions of requests per second, and each response generates a random Request-Id. It’s not exactly 16 bytes, but this is a very realistic scenario where guids are used in hot path. If you are writing a cute-kitten blog, might as well use Python instead..
It generates entropy 4kb-at-a-time (instead of on each call), and uses a cache-pool instead of single cache behind a lock (which is what standard uuid does in "RandPool=ON" mode).
If it isn't meant to follow the RFC, … just find a new word. (There are plenty of alternate schemes out there, too.)
[1]: https://www.rfc-editor.org/rfc/rfc9562.html
This shouldn't really matter as your import paths are obviously different. `github.com/google/uuid` and `github.com/sdrapkin/guid` can happily coexist. Any file/codebase importing both (which would ideally be avoided in the first place) can alias them.
> IMHO "Guid" is just as well known
I think the point the commenter was trying to make is that these do not adhere to the UUID spec. You don't specify which version, but judging by the docs and your comparison to `github.com/google/uuid`, I'd wager most folks looking at this library would assume they are supposed to be V4 UUIDs.
Your link also says that the term UUID predates the founding of Google by over a decade.
You use a UUID when you need a universally unique ID whose guessability properties are not a critical security requirement. While the V4 UUID spec (which this package does not implement, but most users might assume it does) states that a UUID implementation SHOULD be cryptographically secure [1], it also states that they MUST NOT be used as security capabilities [2]. This is b/c they are not intended as secure tokens, but many users mistakenly assume them to be suitable as such. Not to mention, V4 UUIDs only have 122 bits of entropy, not 128, since 6 bits are reserved for version and variant information, which many users don't realize.
So you can generate a UUID that is suitable as a secure token, but at that point don't call it a UUID. Just call it a secure token. And if you need a secure token, use something like Go's `Text()` function from `crypto/rand` [3].
The situation reminds me of how the Go team updated the `math/rand` and `math/rand/v2` packages to use a CSPRNG as a defensive measure [4], while still urging users to use `crypto/rand` in secure contexts.
[1]: https://www.rfc-editor.org/rfc/rfc9562.html#unguessability
[2]: https://www.rfc-editor.org/rfc/rfc9562.html#Security
[3]: https://pkg.go.dev/crypto/rand@go1.24.5#Text
[4]: https://go.dev/blog/chacha8rand
For random token-as-string generation Golang developers should be using https://github.com/sdrapkin/randstring instead of crypto/rand.Text (faster and more flexible).
- This uses global state under the hood. Surprise! Is it thread safe? I’m not a Go expert, but it looks non-thread-safe.
- The copying code reminds me of old-school awful C buffer handling code. Maybe it’s right. Maybe it’s wrong. But it’s not obviously right.
- The actual meat is a cryptographic randomness cache. This is a subtle thing, and all the best practices are missing. Where’s the backtracking protection? What if the program forks? vDSO getrandom() knows how to do this correctly — something high-level should use it, not reimplement it incorrectly.
Unsafe use also looks fine, values either don't escape the function (a type string->byte type cast for function signature reasons) or they do but they're new temporary data (the byte->string cast, which is fine because there's no risk of reusing or modifying the original bytes).
I'm going to intentionally not make any claims to "cryptographic security" or "is this a GUID" as I'm not super clear on the details there. The code looks pretty normal to me though, with the possible exception of the base64 encoding (why not base64.URLEncoding? https://pkg.go.dev/encoding/base64#pkg-variables).
Looks safe to me. It uses `crypto/rand.Read` which is declared as safe for concurrent use. The cache is accessed via sync.Pool which is thread safe. As a check, I ran the tests with `-race` and it passed.
I've been using Cuid2[1] in most of my personal projects (this Go implementation[2], actually), which is fast enough, but not "too fast". It's also secure, collision resistant, and has everything I would need from a UUID.
[1]: https://github.com/paralleldrive/cuid2
[2]: https://github.com/nrednav/cuid2
The only possible think I can think of here is using a UUID version with a small space for the random bits, such that you could accidentally collide by generating them too fast. But with something like UUIDv7, you'd need to be generating hundreds of millions of random UUIDs every nanosecond in order for that to be a realistic concern.
I'm interested in feedback from the HN community.
from your readme, `guid.New()` is 6~10 ns, so presumably the standard UUID package takes 60-100 ns?
say I generate a UUID, and then use that UUID when inserting a row into my database, let's say committing that transaction takes 1 msec (1 million ns)
if I get a speedup of 90 ns from using a faster UUID package, will that even be noticeable in my benchmarks? it seems likely to be lost in the noise.
honestly, this seems like going on a 7-day road trip, and sprinting from your front door to your car because it'll get you there faster.
Dead Comment