{"id":15284351,"url":"https://github.com/danielbankhead/bronze","last_synced_at":"2025-04-12T23:22:05.467Z","repository":{"id":143865802,"uuid":"77646345","full_name":"danielbankhead/bronze","owner":"danielbankhead","description":"Collision-resistant ids for distributed systems","archived":false,"fork":false,"pushed_at":"2018-09-13T16:30:03.000Z","size":194,"stargazers_count":9,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-26T17:21:23.437Z","etag":null,"topics":["altusaero","cassandra","guid","id","identity","ids","javascript","nodejs","timeuuid","uuid","uuid1","uuid4"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danielbankhead.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-12-29T23:37:23.000Z","updated_at":"2024-06-19T19:24:17.000Z","dependencies_parsed_at":null,"dependency_job_id":"146ca477-d14c-4ce1-a7b2-694017ca4a92","html_url":"https://github.com/danielbankhead/bronze","commit_stats":null,"previous_names":["altusaero/bronze"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielbankhead%2Fbronze","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielbankhead%2Fbronze/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielbankhead%2Fbronze/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danielbankhead%2Fbronze/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danielbankhead","download_url":"https://codeload.github.com/danielbankhead/bronze/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248644195,"owners_count":21138566,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["altusaero","cassandra","guid","id","identity","ids","javascript","nodejs","timeuuid","uuid","uuid1","uuid4"],"created_at":"2024-09-30T14:53:59.898Z","updated_at":"2025-04-12T23:22:05.448Z","avatar_url":"https://github.com/danielbankhead.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bronze\n## Collision-resistant ids for distributed systems\n\n[![Bronze by Altus Aero][shield-io-altusaero]][altusaero-github] [![npm version][shield-io-npm-version]][npm] [![npm total downloads][shield-io-npm-total-downloads]][npm] [![npm license][shield-io-npm-license]][npm] [![AppVeyor][shield-io-AppVeyor]][appveyor] [![Travis CI][shield-io-Travis-CI]][travis] [![Travis CI][shield-io-Coveralls]][coveralls] [![GitHub - Issues Open][shield-io-GitHub-Issues-Open]][github-issues] [![GitHub - Pull Requests Open][shield-io-GitHub-Pull-Requests-Open]][github-pulls] [![GitHub - Contributors][shield-io-GitHub-Contributors]][github-graphs-contributors] [![Standard - JavaScript Style Guide][shield-io-standard-style]][standardjs]\n\n[shield-io-altusaero]: https://img.shields.io/badge/altusaero-bronze-C9AE5D.svg?style=flat-square\n[shield-io-npm-version]: https://img.shields.io/npm/v/bronze.svg?style=flat-square\n[shield-io-npm-total-downloads]: https://img.shields.io/npm/dt/bronze.svg?style=flat-square\n[shield-io-npm-license]: https://img.shields.io/npm/l/bronze.svg?style=flat-square\n[shield-io-AppVeyor]: https://img.shields.io/appveyor/ci/DanielBankhead/bronze.svg?style=flat-square\u0026label=appveyor\n[shield-io-Travis-CI]: https://img.shields.io/travis/AltusAero/bronze.svg?style=flat-square\u0026label=travis\n[shield-io-Coveralls]: https://img.shields.io/coveralls/AltusAero/bronze.svg?style=flat-square\n[shield-io-GitHub-Issues-Open]: https://img.shields.io/github/issues-raw/altusaero/bronze.svg?style=flat-square\n[shield-io-GitHub-Pull-Requests-Open]: https://img.shields.io/github/issues-pr-raw/altusaero/bronze.svg?style=flat-square\n[shield-io-GitHub-Contributors]: https://img.shields.io/github/contributors/altusaero/bronze.svg?style=flat-square\n[shield-io-standard-style]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square\n\n[altusaero-github]: https://github.com/altusaero/\n[npm]: https://npmjs.com/package/bronze/\n[appveyor]: https://ci.appveyor.com/project/DanielBankhead/bronze\n[travis]: https://travis-ci.org/AltusAero/bronze\n[coveralls]: https://coveralls.io/github/AltusAero/bronze\n[github-issues]: https://github.com/AltusAero/bronze/issues\n[github-pulls]: https://github.com/AltusAero/bronze/pulls\n[github-graphs-contributors]: https://github.com/AltusAero/bronze/graphs/contributors\n[standardjs]: http://standardjs.com/\n\n\n\n```js\nconst Bronze = require('bronze')\n\nconst idGen = new Bronze({name: 'example'})\n\nconst id = idGen.generate()\n// 1482810226160-0-14210-example-1a\n```\n\n```sh\n$ bronze --name example\n# 1496196550327-31-7887-example-1a\n```\n\n\n## Installation\n\n```sh\n$ npm install bronze\n```\n\nCLI only:\n```sh\n$ [sudo] npm install bronze -g\n```\n\n\n## Features\n\n- designed for distributed and singleton systems\n- no time-based (`UUID1`) or random (`UUID4`) collisions\n- collision resistant - safely generate up to **9,007,199,254,740,991** ids within a single millisecond\n- fast - can generate an id in less than .05ms - _even on old hardware_\n- can be conveniently sorted by `timestamp` or `name`\n- can be used as a module or via CLI\n- supports Node 6+ and browsers\n\n\n## Quick Start\n\n```js\nconst Bronze = require('bronze')\nconst idGen = new Bronze()\n\nidGen.generate()\n// \u003e 1483113483923-0-12179-localhost-1a\nidGen.generate()\n// \u003e 1483113483923-1-12179-localhost-1a\n\n// continue to create ids throughout your code\n```\n\n\n## Specs\n\nA spec determines what goes into an id and how its information is sorted.\n\n`Type 1` - contains a `timestamp` in milliseconds, a `sequence` number, a process id (`PID`), and a `name`\n- `1a` is ordered by `timestamp` - `sequence` - `pid` - `name`\n  - useful for timestamp-based sorting\n  - ex: `1482981306438-0-5132-example-1a`\n- `1b` is ordered by `name` - `pid` - `timestamp` - `sequence`\n  - useful for name-based sorting\n  - ex: `example-5132-1482981317498-0-1b`\n\n\n## Usage\n\n`Bronze` CLASS\n  - _static_ _get_ `defaultName` STRING\n    - the default name for new instances (if not provided)\n\n  - _static_ _get_ `defaultSpec` STRING\n    - the default spec for new instances\n\n  - _static_ _get_ `specs` OBJECT\n    - the default name\n\n  - _static_ `parse` (id)\n    - parses an id to JSON-compatible object\n\n    - `id` STRING **required**\n      - an id to parse\n\n  - _new_ Bronze (options)\n    - Example:\n    ```js\n    const idGen = new Bronze({name: 'example', sequence: 1})\n    ```\n\n    - `options` OBJECT\n      - `sequence` INTEGER\n        - the number of ids generated.\n        - convenient for continuing creating ids where you left off (no pre-increment required)\n        - if invalid falls back to default\n        - _default_ = 0\n      - `pid` INTEGER\n        - a process id to number to use for generated ids\n        - if invalid falls back to default\n        - _default_ = process.pid, else 0\n      - `name` STRING\n        - a unique name for the generator - replaces slashes (\\\\/) with underscores (\\_)\n        - **IMPORTANT** - do not use the same name at the same time on different machines\n        - if invalid falls back to default\n        - _default_ = process.env.HOSTNAME, else _constructor_.defaultName\n      - `spec` STRING\n        - set the spec\n        - if invalid falls back to default\n        - _default_ = _constructor_.defaultSpec\n\n    - _instance_ OBJECT\n      - `sequence` INTEGER\n        - the current sequence\n      - `pid` INTEGER\n        - the pid in use\n      - `name` STRING\n        - the parsed name to be used in ids\n      - `nameRaw` STRING\n        - the raw, pre-parsed name\n      - `spec` STRING\n        - the spec in use\n\n      - `generate` ([options])\n        - generates a new id\n        - Example:\n        ```js\n        const idGen = new Bronze()\n        const id = idGen.generate()\n        // \u003e 1482810226160-0-14210-localhost-1a\n        ```\n\n        - `options` OBJECT _optional_\n          - `json` BOOLEAN\n            - returns results as JSON-compatible object instead of STRING\n            - _default_ = false\n\n        - _return_ id STRING | OBJECT\n          - The generated id\n          - returns an object if `options.json === true`\n\n      - `nextSequence` ()\n        - manually advances the sequence\n        - Example:\n        ```js\n        const idGen = new Bronze()\n        idGen.nextSequence()\n        ```\n\n\u003c!-- TODO:\n  See [examples](examples).\n--\u003e\n## Browser Usage\n\nUsing bronze in the browser is pretty straight-forward. As of [webpack](https://webpack.js.org) 3, no special loaders are required to use bronze. Since most browser environments do not support the `process` object (with the exception of [Electron](https://electron.atom.io), [NW.js](https://nwjs.io), and the like), you should pass the `pid` and `name` options to the constructor, like so:\n\n```js\nnew Bronze({pid: 1, name: 'browser'})\n```\n\nIf you are using bronze in a distributed environment you should verify the generated `name` via `Bronze.parse` in a trusted space, such as the server-side.\n\n\n\n## CLI Usage\n\nThe CLI uses the module under the hood.\n\n```\nUsage: bronze [options]\n\nOptions:\n  --sequence INT          Set the counter for the number of ids generated\n                          By default will use sequence file (sequence path)\n                          If set sequence file will not be updated\n  --pid INT               Set the process id for generated ids\n  --name STRING           A unique name for the generator\n                          Any slashes will be replaced with underscores\n  --spec STRING           Set the spec\n\n  --gen, -g INT           The number of ids to create. Must be \u003e= 0.\n                          Default = 1\n  --list-specs            Get the specs available from this version of bronze\n  --sequence-dir STRING   Set the sequence directory\n                          Will attempt to create if not exist\n  --sequence-dir-reset    Sets the sequence back to 0\n                          File isn't created if it doesn't exist\n\n  --help, -h\n  --version, -v\n```\n\n\n## Motivation\n\nWhile developing a distributed system using `UUID1` and `UUID4` we found that we would run into collisions as we scaled.\n  - `UUID1` (timeuuids) can have collisions within 100ns, which can happen when migrating/importing data in bulk\n  - `UUID4` can have collisions at random. While the risk is reasonably small, a chance of data loss does not sit well with us.\n\n## Goals\n  - no collisions\n    - remove 'randomness' as an element for unique identifiers, which have the possibility of collisions\n    - predictability \u003e random/entropy\n    - even a slim chance is unacceptable, mission-critical apps\n  - keep it simple\n  - fast - should be able to uniquely identify data in real-time apps\n  - having different sorting/grouping options would be nice\n  - a built-in counter (sequence) would be nice for statistics, especially if you can restore it after reboot\n  - compatible with Node's `cluster` module with no additional setup\n  - eliminate need for performance-draining read-before-writes to check if a unique identifier exists\n  - reduced need for counters and auto-incrementing rows\n\n\n## Notes\n  - Node 6+ is required for *1.x*+.\n  - `name` may contain any character, including dashes, but slashes (\\\\/) will be replaced with underscores (\\_).\n    - This allows the opportunity for an id used as a cross-platform filename with little concern.\n  - Every machine in your distributed system should use a unique `name`. If every machine has a unique hostname (`process.env.HOSTNAME`) you should be fine.\n  - To avoid collisions:\n    - Do not reuse a `name` on a different machine within your distributed system's range of clock skew.\n    - Be mindful when changing a system's clock - if moving the clock back temporarily change the `name`\n    - Only one instance of Bronze should be created on a single process (`PID`) to avoid collisions.\n      - Each worker spawned from Node's `cluster` module receives its own `PID` so no worries here.\n  - Sequence counter automatically resets after reaching `Number.MAX_SAFE_INTEGER` (9007199254740991)\n  - Without string parsing, timestamp sorting (`spec 1a`) is supported up to `2286-11-20T17:46:39.999Z` (9999999999999)\n  - Using with databases, such as Cassandra:\n    - most `text` data types should do the trick\n\n\n## Future\n  - CLI\n    - add `--parse` option\n      - JSON output\n  - Nested IDs\n    ```js\n    const id1 = idGen.generate({name: 'example'})\n    console.log(id1)\n    // \u003e 1482810226160-0-14210-example-1a\n\n    // Nested\n    idGen.name = id1\n    const id2 = idGen.generate()\n    console.log(id2)\n    // \u003e 1482810226222-1-14210-1482810226160-0-14210-example-1a-1a\n\n    Bronze.parse('1482810226222-1-14210-1482810226160-0-14210-example-1a-1a')\n    // { valid: true,\n    //   timestamp: 1482810226222,\n    //   sequence: 1,\n    //   pid: 14210,\n    //   name: '1482810226160-0-14210-example-1a',\n    //   spec: '1a' }\n\n    // can also nest id2, id3, id4, id5, ...idN\n    ```\n    - the issue the moment is the possibility of collisions (no unique name)\n    - would be pretty cool - imagine the convenience of nesting a video_id into a comment_id\n\n  - See more in [FUTURE.md](FUTURE.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielbankhead%2Fbronze","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanielbankhead%2Fbronze","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanielbankhead%2Fbronze/lists"}