1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
|
%global _empty_manifest_terminate_build 0
Name: python-timeflake
Version: 0.4.2
Release: 1
Summary: Timeflake is a 128-bit, roughly-ordered, URL-safe UUID. Inspired by Twitter's Snowflake, Instagram's ID and Firebase's PushID.
License: MIT
URL: https://github.com/anthonynsimon/timeflake
Source0: https://mirrors.nju.edu.cn/pypi/web/packages/ee/e6/54df08ad6ef3f62a14f5a0de5fffd45ef3d0fe56ccd8427a2958da4f9dfd/timeflake-0.4.2.tar.gz
BuildArch: noarch
%description
# Timeflake
[](https://pypi.org/project/timeflake/)
[](https://pypi.org/project/timeflake/)
[](https://github.com/anthonynsimon/timeflake/blob/master/LICENSE)
Timeflake is a 128-bit, roughly-ordered, URL-safe UUID. Inspired by Twitter's Snowflake, Instagram's ID and Firebase's PushID.
## Features
- **Fast.** Roughly ordered (K-sortable), incremental timestamp in most significant bits enables faster indexing and less fragmentation on database indices (vs UUID v1/v4).
- **Unique enough.** With 1.2e+24 unique timeflakes per millisecond, even if you're creating 50 million of them *per millisecond* the chance of a collision is still 1 in a billion. You're likely to see a collision when creating 1.3e+12 (one trillion three hundred billion) timeflakes per millisecond.*
- **Efficient.** 128 bits are used to encode a timestamp in milliseconds (48 bits) and a cryptographically generated random number (80 bits).
- **Flexible.** Out of the box encodings in 128-bit unsigned int, hex, URL-safe base62 and raw bytes. Fully compatible with uuid.UUID.
\* Please consider how the [Birthday Paradox](https://betterexplained.com/articles/understanding-the-birthday-paradox/) might affect your use case. Also read the security note on this readme.
## Why?
This could be useful to you, if you're looking for a UUID with the following properties:
- You want to have UUIDs in URLs that are not predictable (vs auto-increment integers).
- They should be random, but roughly-ordered over time so that your MySQL/Postgres indices stay fast and efficient as the dataset grows.
- And simple to use across multiple machines (no coordination or centralized system required).
- It would be nice if they were compatible with standard 128-bit UUID representations (many libraries in Python handle uuid.UUID, but no third-party types).
Some existing alternatives which I considered:
- **UUIDv1** but the timestamp bytes are not sequential and gives away network information.
- **UUIDv4** but they're mostly random, and can mess up the performance on clustered indexes.
- **ULID** but the approach to incrementing the sequence during the same millisecond makes it more predictable.
- **KSUID** but it's 160-bit, so unfortunately not compatible with standard 128-bit UUIDs.
## Usage
```python
import timeflake
# Create a random Timeflake
flake = timeflake.random()
>>> Timeflake(base62='00mx79Rjxvfgr8qat2CeQDs')
# Get the base62, int, hex or bytes representation
flake.base62
>>> '00mx79Rjxvfgr8qat2CeQDs'
flake.hex
>>> '016fa936bff0997a0a3c428548fee8c9'
flake.int
>>> 1909005012028578488143182045514754249
flake.bytes
>>> b'\x01o\xa96\xbf\xf0\x99z\n<B\x85H\xfe\xe8\xc9'
# Convert to the standard library's UUID type
flake.uuid
>>> UUID('016fa936-bff0-997a-0a3c-428548fee8c9')
# Get the timestamp component
flake.timestamp
>>> 1579091935216
# Get the random component
flake.random
>>> 724773312193627487660233
# Parse an existing flake (you can also pass bytes, hex or int representations)
timeflake.parse(from_base62='0002HCZffkHWhKPVdXxs0YH')
>>> Timeflake('0004fbc6872f70fc9e27355a499e8b6d')
# Create from a user defined timestamp or random value:
timeflake.from_values(1579091935216, 724773312193627487660233)
>>> Timeflake('016fa936bff0997a0a3c428548fee8c9')
```
## Components
The timeflake `02i2XhN7hAuaFh3MwztcMd` (base62) encodes the following:
```
# Milliseconds since unix epoch
timestamp = 1579275030563
# Cryptographically generated random number
random = 851298578153087956398315
```
## Alphabets
A custom base62 alphabet representation is included, modified to preserve lexicographical order when sorting strings using this encoding. The `hex` representation has a max length of 32 characters, while the `base62` will be 22 characters. Padding is required to be able to derive the encoding from the string length.
The following are all valid representations of the same Timeflake:
```
int = 1909226360721144613344160656901255403
hex = 016fb4209023b444fd07590f81b7b0eb
base62 = 02i2XhN7hAuaFh3MwztcMd
```
## Provided extensions
### Django model fields
You can use timeflakes as primary keys for your models. These fields currently support MySQL, Postgres and Sqlite3.
Example usage:
```python
from timeflake.extensions.django import TimeflakePrimaryKeyBinary
class Item(models.Model):
item_id = TimeflakePrimaryKeyBinary()
# ...
```
### Peewee ORM
See [this gist](https://gist.github.com/bibby/3c7a162fb83a833850af8ff668d5441b) for an example.
## Note on security
Since the timestamp part is predictable, the search space within any given millisecond is 2^80 random numbers, which is meant to avoid collisions, not to secure or hide information. You should not be using timeflakes for password-reset tokens, API keys or for anything which is security sensitive. There are better libraries which are meant for this use case (for example, the standard library's [secrets module](https://docs.python.org/3/library/secrets.html)).
## Note on privacy
Please be aware of the privacy implications that time based IDs can have. As Timeflake encodes the precise time in which the ID was created, this could potentially reveal:
- User timezone.
- Geographic location: If the client software creates multiple associated IDs at the same time (like an article and embedded media), then the differences in timestamps of the IDs can reveal the latency of the client's network connection to the server. This reveals user geographic location. This can also happen if the client creates a single ID and the server adds an additional timestamp to the object.
- User identity (de-anonymizing)
1. Most Android apps include Google's libraries for working with push notifications. And some iOS apps that use Google Cloud services also load the libraries. These Google libraries automatically load Google Analytics which records the names of every screen the users view in the app, and sends them to Google. So Google knows that userN switched from screen "New Post" to screen "Published Post" at time K.
2. Some ISPs record and sell user behavior data. For example, SAP knows that userN made a request to appM's API at time K.
3. Even if the posting app does not share its user behavior data with third-parties, the user could post and then immediately switch to an app that does share user behavior data. This provides data points like "userN stopped using an app that does not record analytics at time K".
4. Operating Systems (Android, Windows, macOS) send user behavior data to their respective companies.
5. Browsers and Browser Extensions send user behavior data to many companies. Data points like "userN visited a URL at example.com at time K" can end up in many databases and sold.
6. Posting times combined with traffic analysis can perfectly de-anonymize users.
- How long the user took to write the post. This can happen if the app creates the ID when the user starts editing the post and also shares a timestamp of the publication or save time.
- Whether or not the user edited the post after posting it. This can happen if the posts's displayed time doesn't match the timestamp in the ID.
- Whether or not the user prepared the post in advance and set it to post automatically. If the timestamp is very close to a round numbered time like 21:00:00, it was likely posted automatically. If the posting platform does not provide such functionality, then the user must be using some third-party software or custom software to do it. This information can help de-anonymize the user.
## Supported versions
Right now the codebase is only tested with Python 3.7+.
## Dependencies
No dependencies other than the standard library.
## Contribute
Want to hack on the project? Any kind of contribution is welcome!
Simply follow the next steps:
- Fork the project.
- Create a new branch.
- Make your changes and write tests when practical.
- Commit your changes to the new branch.
- Send a pull request, it will be reviewed shortly.
- In case you want to add a feature, please create a new issue and briefly explain what the feature would consist of. For bugs or requests, before creating an issue please check if one has already been created for it.
## Contributors
Thank you for making this project better!
- [@mleonhard](https://github.com/mleonhard) - documented privacy implications of time based IDs.
- [@making](https://github.com/making) - Implemented Java version of Timeflake.
- [@Gioni06](https://github.com/Gioni06) - Implemented Go version of Timeflake.
- [@zzzz465](https://github.com/zzzz465) - Implementation of TS/JS version of Timeflake.
- [@bibby](https://github.com/bibby) - Added extension for peewee ORM.
- [@sebst](https://github.com/sebst) - Improved compatibility with standard UUID class.
- [@yedpodtrzitko](https://github.com/yedpodtrzitko) - Codebase improvements.
## Changelog
Please see the [CHANGELOG](CHANGELOG.md) for more details.
## Implementations in other languages
* [Java](https://github.com/making/timeflake4j)
* [Go](https://github.com/Gioni06/go-timeflake)
* [Javascript/Typescript](https://github.com/zzzz465/timeflake4js)
## License
This project is licensed under the MIT license. Please read the [LICENSE](LICENSE) file for more details.
## Prior art
- [Sharding & IDs at Instagram](https://instagram-engineering.com/sharding-ids-at-instagram-1cf5a71e5a5c)
- [Announcing Snowflake: Twitter](https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake.html)
- [The 2^120 Ways to Ensure Unique Identifiers](https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html)
%package -n python3-timeflake
Summary: Timeflake is a 128-bit, roughly-ordered, URL-safe UUID. Inspired by Twitter's Snowflake, Instagram's ID and Firebase's PushID.
Provides: python-timeflake
BuildRequires: python3-devel
BuildRequires: python3-setuptools
BuildRequires: python3-pip
%description -n python3-timeflake
# Timeflake
[](https://pypi.org/project/timeflake/)
[](https://pypi.org/project/timeflake/)
[](https://github.com/anthonynsimon/timeflake/blob/master/LICENSE)
Timeflake is a 128-bit, roughly-ordered, URL-safe UUID. Inspired by Twitter's Snowflake, Instagram's ID and Firebase's PushID.
## Features
- **Fast.** Roughly ordered (K-sortable), incremental timestamp in most significant bits enables faster indexing and less fragmentation on database indices (vs UUID v1/v4).
- **Unique enough.** With 1.2e+24 unique timeflakes per millisecond, even if you're creating 50 million of them *per millisecond* the chance of a collision is still 1 in a billion. You're likely to see a collision when creating 1.3e+12 (one trillion three hundred billion) timeflakes per millisecond.*
- **Efficient.** 128 bits are used to encode a timestamp in milliseconds (48 bits) and a cryptographically generated random number (80 bits).
- **Flexible.** Out of the box encodings in 128-bit unsigned int, hex, URL-safe base62 and raw bytes. Fully compatible with uuid.UUID.
\* Please consider how the [Birthday Paradox](https://betterexplained.com/articles/understanding-the-birthday-paradox/) might affect your use case. Also read the security note on this readme.
## Why?
This could be useful to you, if you're looking for a UUID with the following properties:
- You want to have UUIDs in URLs that are not predictable (vs auto-increment integers).
- They should be random, but roughly-ordered over time so that your MySQL/Postgres indices stay fast and efficient as the dataset grows.
- And simple to use across multiple machines (no coordination or centralized system required).
- It would be nice if they were compatible with standard 128-bit UUID representations (many libraries in Python handle uuid.UUID, but no third-party types).
Some existing alternatives which I considered:
- **UUIDv1** but the timestamp bytes are not sequential and gives away network information.
- **UUIDv4** but they're mostly random, and can mess up the performance on clustered indexes.
- **ULID** but the approach to incrementing the sequence during the same millisecond makes it more predictable.
- **KSUID** but it's 160-bit, so unfortunately not compatible with standard 128-bit UUIDs.
## Usage
```python
import timeflake
# Create a random Timeflake
flake = timeflake.random()
>>> Timeflake(base62='00mx79Rjxvfgr8qat2CeQDs')
# Get the base62, int, hex or bytes representation
flake.base62
>>> '00mx79Rjxvfgr8qat2CeQDs'
flake.hex
>>> '016fa936bff0997a0a3c428548fee8c9'
flake.int
>>> 1909005012028578488143182045514754249
flake.bytes
>>> b'\x01o\xa96\xbf\xf0\x99z\n<B\x85H\xfe\xe8\xc9'
# Convert to the standard library's UUID type
flake.uuid
>>> UUID('016fa936-bff0-997a-0a3c-428548fee8c9')
# Get the timestamp component
flake.timestamp
>>> 1579091935216
# Get the random component
flake.random
>>> 724773312193627487660233
# Parse an existing flake (you can also pass bytes, hex or int representations)
timeflake.parse(from_base62='0002HCZffkHWhKPVdXxs0YH')
>>> Timeflake('0004fbc6872f70fc9e27355a499e8b6d')
# Create from a user defined timestamp or random value:
timeflake.from_values(1579091935216, 724773312193627487660233)
>>> Timeflake('016fa936bff0997a0a3c428548fee8c9')
```
## Components
The timeflake `02i2XhN7hAuaFh3MwztcMd` (base62) encodes the following:
```
# Milliseconds since unix epoch
timestamp = 1579275030563
# Cryptographically generated random number
random = 851298578153087956398315
```
## Alphabets
A custom base62 alphabet representation is included, modified to preserve lexicographical order when sorting strings using this encoding. The `hex` representation has a max length of 32 characters, while the `base62` will be 22 characters. Padding is required to be able to derive the encoding from the string length.
The following are all valid representations of the same Timeflake:
```
int = 1909226360721144613344160656901255403
hex = 016fb4209023b444fd07590f81b7b0eb
base62 = 02i2XhN7hAuaFh3MwztcMd
```
## Provided extensions
### Django model fields
You can use timeflakes as primary keys for your models. These fields currently support MySQL, Postgres and Sqlite3.
Example usage:
```python
from timeflake.extensions.django import TimeflakePrimaryKeyBinary
class Item(models.Model):
item_id = TimeflakePrimaryKeyBinary()
# ...
```
### Peewee ORM
See [this gist](https://gist.github.com/bibby/3c7a162fb83a833850af8ff668d5441b) for an example.
## Note on security
Since the timestamp part is predictable, the search space within any given millisecond is 2^80 random numbers, which is meant to avoid collisions, not to secure or hide information. You should not be using timeflakes for password-reset tokens, API keys or for anything which is security sensitive. There are better libraries which are meant for this use case (for example, the standard library's [secrets module](https://docs.python.org/3/library/secrets.html)).
## Note on privacy
Please be aware of the privacy implications that time based IDs can have. As Timeflake encodes the precise time in which the ID was created, this could potentially reveal:
- User timezone.
- Geographic location: If the client software creates multiple associated IDs at the same time (like an article and embedded media), then the differences in timestamps of the IDs can reveal the latency of the client's network connection to the server. This reveals user geographic location. This can also happen if the client creates a single ID and the server adds an additional timestamp to the object.
- User identity (de-anonymizing)
1. Most Android apps include Google's libraries for working with push notifications. And some iOS apps that use Google Cloud services also load the libraries. These Google libraries automatically load Google Analytics which records the names of every screen the users view in the app, and sends them to Google. So Google knows that userN switched from screen "New Post" to screen "Published Post" at time K.
2. Some ISPs record and sell user behavior data. For example, SAP knows that userN made a request to appM's API at time K.
3. Even if the posting app does not share its user behavior data with third-parties, the user could post and then immediately switch to an app that does share user behavior data. This provides data points like "userN stopped using an app that does not record analytics at time K".
4. Operating Systems (Android, Windows, macOS) send user behavior data to their respective companies.
5. Browsers and Browser Extensions send user behavior data to many companies. Data points like "userN visited a URL at example.com at time K" can end up in many databases and sold.
6. Posting times combined with traffic analysis can perfectly de-anonymize users.
- How long the user took to write the post. This can happen if the app creates the ID when the user starts editing the post and also shares a timestamp of the publication or save time.
- Whether or not the user edited the post after posting it. This can happen if the posts's displayed time doesn't match the timestamp in the ID.
- Whether or not the user prepared the post in advance and set it to post automatically. If the timestamp is very close to a round numbered time like 21:00:00, it was likely posted automatically. If the posting platform does not provide such functionality, then the user must be using some third-party software or custom software to do it. This information can help de-anonymize the user.
## Supported versions
Right now the codebase is only tested with Python 3.7+.
## Dependencies
No dependencies other than the standard library.
## Contribute
Want to hack on the project? Any kind of contribution is welcome!
Simply follow the next steps:
- Fork the project.
- Create a new branch.
- Make your changes and write tests when practical.
- Commit your changes to the new branch.
- Send a pull request, it will be reviewed shortly.
- In case you want to add a feature, please create a new issue and briefly explain what the feature would consist of. For bugs or requests, before creating an issue please check if one has already been created for it.
## Contributors
Thank you for making this project better!
- [@mleonhard](https://github.com/mleonhard) - documented privacy implications of time based IDs.
- [@making](https://github.com/making) - Implemented Java version of Timeflake.
- [@Gioni06](https://github.com/Gioni06) - Implemented Go version of Timeflake.
- [@zzzz465](https://github.com/zzzz465) - Implementation of TS/JS version of Timeflake.
- [@bibby](https://github.com/bibby) - Added extension for peewee ORM.
- [@sebst](https://github.com/sebst) - Improved compatibility with standard UUID class.
- [@yedpodtrzitko](https://github.com/yedpodtrzitko) - Codebase improvements.
## Changelog
Please see the [CHANGELOG](CHANGELOG.md) for more details.
## Implementations in other languages
* [Java](https://github.com/making/timeflake4j)
* [Go](https://github.com/Gioni06/go-timeflake)
* [Javascript/Typescript](https://github.com/zzzz465/timeflake4js)
## License
This project is licensed under the MIT license. Please read the [LICENSE](LICENSE) file for more details.
## Prior art
- [Sharding & IDs at Instagram](https://instagram-engineering.com/sharding-ids-at-instagram-1cf5a71e5a5c)
- [Announcing Snowflake: Twitter](https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake.html)
- [The 2^120 Ways to Ensure Unique Identifiers](https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html)
%package help
Summary: Development documents and examples for timeflake
Provides: python3-timeflake-doc
%description help
# Timeflake
[](https://pypi.org/project/timeflake/)
[](https://pypi.org/project/timeflake/)
[](https://github.com/anthonynsimon/timeflake/blob/master/LICENSE)
Timeflake is a 128-bit, roughly-ordered, URL-safe UUID. Inspired by Twitter's Snowflake, Instagram's ID and Firebase's PushID.
## Features
- **Fast.** Roughly ordered (K-sortable), incremental timestamp in most significant bits enables faster indexing and less fragmentation on database indices (vs UUID v1/v4).
- **Unique enough.** With 1.2e+24 unique timeflakes per millisecond, even if you're creating 50 million of them *per millisecond* the chance of a collision is still 1 in a billion. You're likely to see a collision when creating 1.3e+12 (one trillion three hundred billion) timeflakes per millisecond.*
- **Efficient.** 128 bits are used to encode a timestamp in milliseconds (48 bits) and a cryptographically generated random number (80 bits).
- **Flexible.** Out of the box encodings in 128-bit unsigned int, hex, URL-safe base62 and raw bytes. Fully compatible with uuid.UUID.
\* Please consider how the [Birthday Paradox](https://betterexplained.com/articles/understanding-the-birthday-paradox/) might affect your use case. Also read the security note on this readme.
## Why?
This could be useful to you, if you're looking for a UUID with the following properties:
- You want to have UUIDs in URLs that are not predictable (vs auto-increment integers).
- They should be random, but roughly-ordered over time so that your MySQL/Postgres indices stay fast and efficient as the dataset grows.
- And simple to use across multiple machines (no coordination or centralized system required).
- It would be nice if they were compatible with standard 128-bit UUID representations (many libraries in Python handle uuid.UUID, but no third-party types).
Some existing alternatives which I considered:
- **UUIDv1** but the timestamp bytes are not sequential and gives away network information.
- **UUIDv4** but they're mostly random, and can mess up the performance on clustered indexes.
- **ULID** but the approach to incrementing the sequence during the same millisecond makes it more predictable.
- **KSUID** but it's 160-bit, so unfortunately not compatible with standard 128-bit UUIDs.
## Usage
```python
import timeflake
# Create a random Timeflake
flake = timeflake.random()
>>> Timeflake(base62='00mx79Rjxvfgr8qat2CeQDs')
# Get the base62, int, hex or bytes representation
flake.base62
>>> '00mx79Rjxvfgr8qat2CeQDs'
flake.hex
>>> '016fa936bff0997a0a3c428548fee8c9'
flake.int
>>> 1909005012028578488143182045514754249
flake.bytes
>>> b'\x01o\xa96\xbf\xf0\x99z\n<B\x85H\xfe\xe8\xc9'
# Convert to the standard library's UUID type
flake.uuid
>>> UUID('016fa936-bff0-997a-0a3c-428548fee8c9')
# Get the timestamp component
flake.timestamp
>>> 1579091935216
# Get the random component
flake.random
>>> 724773312193627487660233
# Parse an existing flake (you can also pass bytes, hex or int representations)
timeflake.parse(from_base62='0002HCZffkHWhKPVdXxs0YH')
>>> Timeflake('0004fbc6872f70fc9e27355a499e8b6d')
# Create from a user defined timestamp or random value:
timeflake.from_values(1579091935216, 724773312193627487660233)
>>> Timeflake('016fa936bff0997a0a3c428548fee8c9')
```
## Components
The timeflake `02i2XhN7hAuaFh3MwztcMd` (base62) encodes the following:
```
# Milliseconds since unix epoch
timestamp = 1579275030563
# Cryptographically generated random number
random = 851298578153087956398315
```
## Alphabets
A custom base62 alphabet representation is included, modified to preserve lexicographical order when sorting strings using this encoding. The `hex` representation has a max length of 32 characters, while the `base62` will be 22 characters. Padding is required to be able to derive the encoding from the string length.
The following are all valid representations of the same Timeflake:
```
int = 1909226360721144613344160656901255403
hex = 016fb4209023b444fd07590f81b7b0eb
base62 = 02i2XhN7hAuaFh3MwztcMd
```
## Provided extensions
### Django model fields
You can use timeflakes as primary keys for your models. These fields currently support MySQL, Postgres and Sqlite3.
Example usage:
```python
from timeflake.extensions.django import TimeflakePrimaryKeyBinary
class Item(models.Model):
item_id = TimeflakePrimaryKeyBinary()
# ...
```
### Peewee ORM
See [this gist](https://gist.github.com/bibby/3c7a162fb83a833850af8ff668d5441b) for an example.
## Note on security
Since the timestamp part is predictable, the search space within any given millisecond is 2^80 random numbers, which is meant to avoid collisions, not to secure or hide information. You should not be using timeflakes for password-reset tokens, API keys or for anything which is security sensitive. There are better libraries which are meant for this use case (for example, the standard library's [secrets module](https://docs.python.org/3/library/secrets.html)).
## Note on privacy
Please be aware of the privacy implications that time based IDs can have. As Timeflake encodes the precise time in which the ID was created, this could potentially reveal:
- User timezone.
- Geographic location: If the client software creates multiple associated IDs at the same time (like an article and embedded media), then the differences in timestamps of the IDs can reveal the latency of the client's network connection to the server. This reveals user geographic location. This can also happen if the client creates a single ID and the server adds an additional timestamp to the object.
- User identity (de-anonymizing)
1. Most Android apps include Google's libraries for working with push notifications. And some iOS apps that use Google Cloud services also load the libraries. These Google libraries automatically load Google Analytics which records the names of every screen the users view in the app, and sends them to Google. So Google knows that userN switched from screen "New Post" to screen "Published Post" at time K.
2. Some ISPs record and sell user behavior data. For example, SAP knows that userN made a request to appM's API at time K.
3. Even if the posting app does not share its user behavior data with third-parties, the user could post and then immediately switch to an app that does share user behavior data. This provides data points like "userN stopped using an app that does not record analytics at time K".
4. Operating Systems (Android, Windows, macOS) send user behavior data to their respective companies.
5. Browsers and Browser Extensions send user behavior data to many companies. Data points like "userN visited a URL at example.com at time K" can end up in many databases and sold.
6. Posting times combined with traffic analysis can perfectly de-anonymize users.
- How long the user took to write the post. This can happen if the app creates the ID when the user starts editing the post and also shares a timestamp of the publication or save time.
- Whether or not the user edited the post after posting it. This can happen if the posts's displayed time doesn't match the timestamp in the ID.
- Whether or not the user prepared the post in advance and set it to post automatically. If the timestamp is very close to a round numbered time like 21:00:00, it was likely posted automatically. If the posting platform does not provide such functionality, then the user must be using some third-party software or custom software to do it. This information can help de-anonymize the user.
## Supported versions
Right now the codebase is only tested with Python 3.7+.
## Dependencies
No dependencies other than the standard library.
## Contribute
Want to hack on the project? Any kind of contribution is welcome!
Simply follow the next steps:
- Fork the project.
- Create a new branch.
- Make your changes and write tests when practical.
- Commit your changes to the new branch.
- Send a pull request, it will be reviewed shortly.
- In case you want to add a feature, please create a new issue and briefly explain what the feature would consist of. For bugs or requests, before creating an issue please check if one has already been created for it.
## Contributors
Thank you for making this project better!
- [@mleonhard](https://github.com/mleonhard) - documented privacy implications of time based IDs.
- [@making](https://github.com/making) - Implemented Java version of Timeflake.
- [@Gioni06](https://github.com/Gioni06) - Implemented Go version of Timeflake.
- [@zzzz465](https://github.com/zzzz465) - Implementation of TS/JS version of Timeflake.
- [@bibby](https://github.com/bibby) - Added extension for peewee ORM.
- [@sebst](https://github.com/sebst) - Improved compatibility with standard UUID class.
- [@yedpodtrzitko](https://github.com/yedpodtrzitko) - Codebase improvements.
## Changelog
Please see the [CHANGELOG](CHANGELOG.md) for more details.
## Implementations in other languages
* [Java](https://github.com/making/timeflake4j)
* [Go](https://github.com/Gioni06/go-timeflake)
* [Javascript/Typescript](https://github.com/zzzz465/timeflake4js)
## License
This project is licensed under the MIT license. Please read the [LICENSE](LICENSE) file for more details.
## Prior art
- [Sharding & IDs at Instagram](https://instagram-engineering.com/sharding-ids-at-instagram-1cf5a71e5a5c)
- [Announcing Snowflake: Twitter](https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake.html)
- [The 2^120 Ways to Ensure Unique Identifiers](https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html)
%prep
%autosetup -n timeflake-0.4.2
%build
%py3_build
%install
%py3_install
install -d -m755 %{buildroot}/%{_pkgdocdir}
if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
pushd %{buildroot}
if [ -d usr/lib ]; then
find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
fi
if [ -d usr/lib64 ]; then
find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
fi
if [ -d usr/bin ]; then
find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
fi
if [ -d usr/sbin ]; then
find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
fi
touch doclist.lst
if [ -d usr/share/man ]; then
find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
fi
popd
mv %{buildroot}/filelist.lst .
mv %{buildroot}/doclist.lst .
%files -n python3-timeflake -f filelist.lst
%dir %{python3_sitelib}/*
%files help -f doclist.lst
%{_docdir}/*
%changelog
* Fri May 05 2023 Python_Bot <Python_Bot@openeuler.org> - 0.4.2-1
- Package Spec generated
|