Mehr Unique ID (MUID)
64-bit BigInt database IDs, Base36 case-insensitive identifiers, and 256-bit Base62 account authentication tokens.
MUID Technical Specification
Formal technical architecture, Tehran epoch mathematics, 64-bit BigInt database layout, Base36 identifiers, and 256-bit authentication tokens.
1. 64-Bit Numeric Database Identifier
Engine ArchitectureMUIDs are natively stored as standard 64-bit signed integers (SQL BIGINT). Every identifier combines elapsed seconds from the fixed Tehran epoch with four decimal random sequence digits:
شناسههای MUID در پایگاه داده به صورت اعداد صحیح ۶۴ بیتی با علامت (SQL BIGINT) ذخیره میشوند. هر شناسه از ترکیب ثانیههای سپریشده از مبدا تهران و چهار رقم تصادفی تشکیل میشود:
- Fixed Epoch:
2026-09-22 00:00:00 (UTC+3:30)= Unix timestamp1790022600. - Throughput Capacity: 10,000 unique sequence slots per second per generator node before collision risk.
- Official Verification Vectors:
13954201234↔ Base366eryyky|390889398↔ Base366gq46u
- مبدا ثابت زمانی:
۲۰۲۶-۰۹-۲۲ ۰۰:۰۰:۰۰ (UTC+3:30)برابر با تایماستمپ یونیکس۱۷۹۰۰۲۲۶۰۰. - ظرفیت تولید: ۱۰٬۰۰۰ جایگاه تصادفی در هر ثانیه به ازای هر نود تولیدکننده بدون تداخل.
- نمونههای آزمون رسمی:
13954201234↔ در مبنای ۳۶ برابر با6eryyky|390889398↔ در مبنای ۳۶ برابر با6gq46u
2. Public Account ID (Base36)
While databases store the native 64-bit integer, all public-facing APIs, URLs, user interfaces, and customer interactions use Base36:
- Alphabet:
0123456789abcdefghijklmnopqrstuvwxyz(36 characters). - Case-Insensitivity: Normalized via lowercasing. Both
6gq46uand6GQ46Uresolve to the identical raw integer (390889398). - URL & Speech Safe: No hyphens, slashes, or special symbols. Safe for email links, phone support, and printed QR codes.
- Compact Length: Starts at 6 characters and expands to 12 characters over centuries.
در حالی که پایگاه داده عدد ۶۴ بیتی را ذخیره میکند، تمام APIهای عمومی، آدرسهای وب و تعاملات کاربران از Base36 استفاده میکنند:
- الفبا:
0123456789abcdefghijklmnopqrstuvwxyz(۳۶ کاراکتر). - عدم حساسیت به حروف کوچک و بزرگ: هر دو شکل
6gq46uو6GQ46Uبه یک عدد مشخص (390889398) اشاره دارند. - امن برای آدرس وب و تلفن: فاقد خط تیره یا نشانههای خاص، مناسب برای خواندن تلفنی، پیامک و لینکها.
- طول بهینه: با ۶ کاراکتر شروع شده و برای قرنها حداکثر به ۱۲ کاراکتر میرسد.
3. 256-Bit Secret & API Regex
Authentication decouples identification from authorization. Each account holds a 256-bit cryptographic secret encoded in Base62:
- CSPRNG Entropy: 32 secure random bytes (256 bits, equivalent to AES-256).
- Base62 Encoding: Big-endian conversion (
0-9A-Za-z) fixed to exactly 43 characters. - Account Token: Formatted as
"<account id>:<account secret>".
احراز هویت، شناسه کاربری را از رمز دسترسی جدا میکند. هر حساب دارای یک رمز ۲۵۶ بیتی امنیتی بر مبنای Base62 است:
- آنتروپی امنیتی: ۳۲ بایت تصادفی رمزنگاریشده (۲۵۶ بیت، همتراز با استاندارد AES-256).
- کدگذاری Base62: تبدیل با الفبای
0-9A-Za-zبا طول ثابت دقیقاً ۴۳ کاراکتر. - توکن حساب: به قالب
"<account id>:<account secret>"ساخته میشود.
The Genesis Epoch Decision (Why 2026-09-22?)
Day 0 OffsetThe official public launch of the Mehr network was scheduled for 2026-09-23 (corresponding to 2585-07-01, or 1 Mehr 2585 in the Imperial calendar / 1405 SH). The MUID timeline begins exactly one day earlier at 2026-09-22 00:00:00 (UTC+3:30) to anchor the epoch at Day 0 (corresponding to 2585-06-31 / 31 Shahrivar):
تاریخ انتشار رسمی و عمومی شبکه مهر، روز ۲۰۲۶-۰۹-۲۳ برابر با ۲۵۸۵-۰۷-۰۱ (۱ مهر ۲۵۸۵ گاهشماری شاهنشاهی / ۱۴۰۵ خورشیدی) تعیین شد. مبدا زمانی MUID دقیقا یک روز قبل در ۲۰۲۶-۰۹-۲۲ ۰۰:۰۰:۰۰ (UTC+3:30) آغاز میشود تا تقویم شناسهها بر روی روز صفر (برابر با ۲۵۸۵-۰۶-۳۱ / ۳۱ شهریور) پایهگذاری گردد:
The timeline begins at 00:00:00 (UTC+3:30) (Unix timestamp 1790022600). Genesis state, system bootstrapping, and initial node migrations execute cleanly within this 0-indexed day without producing negative offsets.
The official release of Mehr begins. User accounts created on launch day start from Day 1 (after 86,400s), ensuring all public identifiers follow clean positive natural numbers.
4. Database Architecture & Production DDL
Zero Index FragmentationBecause MUID is time-ordered (K-sortable), B-tree primary key indexes append monotonically at the right leaf edge. This avoids random B-tree page splits, reduces WAL volume, and cuts disk consumption by 50% compared to 128-bit UUIDs.
به دلیل ساختار ترتیبی مبتنی بر زمان (K-sortable)، ایندکسهای درخت B پایگاه داده به صورت ترتیبی رشد میکنند. این امر از شکستن تصادفی صفحات حافظه جلوگیری کرده و مصرف دیسک را در مقایسه با UUIDهای ۱۲۸ بیتی به نصف کاهش میدهد.
CREATE TABLE accounts ( id BIGINT PRIMARY KEY, secret CHAR(43) NOT NULL, created_at TIMESTAMPTZ DEFAULT clock_timestamp() );
CREATE TABLE accounts ( id BIGINT UNSIGNED PRIMARY KEY, secret CHAR(43) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB;
CREATE TABLE accounts ( id INTEGER PRIMARY KEY, secret TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );
CREATE TABLE accounts ( id UInt64, secret FixedString(43), created_at DateTime DEFAULT now() ) ENGINE = MergeTree() ORDER BY id;
5. System Comparison Matrix
Standard Evaluation| Identifier System | Storage Size | Public Representation | Case Sensitivity | K-Sortable | Decoupled Secret | Collision Strategy |
|---|---|---|---|---|---|---|
| Mehr Unique ID (MUID) | 8 Bytes (BigInt) | Base36 (6–12 chars) | Insensitive | Yes | 256-bit Built-in | 10,000 slots / sec / node |
| UUIDv4 (RFC 4122) | 16 Bytes | Hex Hyphenated (36 chars) | Insensitive | No (Random) | None | 122 bits pseudo-random |
| UUIDv7 (RFC 9562) | 16 Bytes | Hex Hyphenated (36 chars) | Insensitive | Yes (ms) | None | Unix ms + sub-ms random |
| ULID | 16 Bytes | Crockford Base32 (26 chars) | Insensitive | Yes (ms) | None | 80-bit random per ms |
| Twitter Snowflake | 8 Bytes | Decimal String (19 chars) | Numeric | Yes (ms) | None | Coordinated Node ID + Seq |
| NanoID | Variable (16–21 B) | Custom Base64 (21 chars) | Sensitive | No (Random) | None | Uniform distribution |
Implementation Examples
Self-contained production implementations in Go, JavaScript, TypeScript, Python, PHP, and POSIX Shell.
1package muid2 3import (4 "crypto/rand"5 "fmt"6 "math/big"7 "regexp"8 "strconv"9 "strings"10 "time"11)12 13const (14 EpochTimestamp = 1790022600 // 2026-09-22 00:00:00 UTC+3:30 (Day 0)15 Base62Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"16)17 18var APITokenRegex = regexp.MustCompile(`^[0-9a-z]{6,12}:[0-9a-zA-Z]{43}$`)19 20func Generate() (raw int64, b36 string, err error) {21 elapsed := time.Now().Unix() - EpochTimestamp22 rBig, err := rand.Int(rand.Reader, big.NewInt(10000))23 if err != nil {24 return 0, "", err25 }26 raw = elapsed*10000 + rBig.Int64()27 return raw, strconv.FormatInt(raw, 36), nil28}29 30func Parse(b36 string) (raw int64, createdAt time.Time, randomSuffix int, err error) {31 raw, err = strconv.ParseInt(strings.ToLower(strings.TrimSpace(b36)), 36, 64)32 if err != nil {33 return 0, time.Time{}, 0, err34 }35 return raw, time.Unix(EpochTimestamp+(raw/10000), 0).UTC(), int(raw % 10000), nil36}37 38func GenerateSecret() (string, error) {39 b := make([]byte, 32)40 if _, err := rand.Read(b); err != nil {41 return "", err42 }43 n := new(big.Int).SetBytes(b)44 base := big.NewInt(62)45 rem := new(big.Int)46 var sb strings.Builder47 for n.Sign() > 0 {48 n.DivMod(n, base, rem)49 sb.WriteByte(Base62Chars[rem.Int64()])50 }51 runes := []rune(sb.String())52 for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {53 runes[i], runes[j] = runes[j], runes[i]54 }55 res := string(runes)56 for len(res) < 43 {57 res = "0" + res58 }59 return res, nil60}
1const EPOCH = 1790022600n; // 2026-09-22 00:00:00 UTC+3:30 (Day 0)2const B62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";3export const API_TOKEN_REGEX = /^[0-9a-z]{6,12}:[0-9a-zA-Z]{43}$/;4 5export function generateMuid() {6 const elapsed = BigInt(Math.floor(Date.now() / 1000)) - EPOCH;7 const rand = BigInt(crypto.getRandomValues(new Uint32Array(1))[0] % 10000);8 const raw = elapsed * 10000n + rand;9 return { raw, base36: raw.toString(36) };10}11 12export function parseMuid(b36) {13 const clean = String(b36).trim().toLowerCase();14 let raw = 0n;15 for (const ch of clean) {16 const code = ch.charCodeAt(0);17 raw = raw * 36n + BigInt(code >= 48 && code <= 57 ? code - 48 : code - 87);18 }19 return { raw, base36: raw.toString(36), elapsed: raw / 10000n, rand: Number(raw % 10000n) };20}21 22export function generateSecret() {23 const bytes = crypto.getRandomValues(new Uint8Array(32));24 let num = 0n;25 for (const b of bytes) num = (num << 8n) | BigInt(b);26 let res = "";27 while (num > 0n) {28 res = B62[Number(num % 62n)] + res;29 num /= 62n;30 }31 return res.padStart(43, "0");32}
1export const EPOCH: bigint = 1790022600n; // 2026-09-22 (Day 0)2export const API_TOKEN_REGEX = /^[0-9a-z]{6,12}:[0-9a-zA-Z]{43}$/;3const B62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";4 5export function generateMuid(): { raw: bigint; base36: string } {6 const elapsed = BigInt(Math.floor(Date.now() / 1000)) - EPOCH;7 const rand = BigInt(crypto.getRandomValues(new Uint32Array(1))[0] % 10000);8 const raw = elapsed * 10000n + rand;9 return { raw, base36: raw.toString(36) };10}11 12export function parseMuid(b36: string): { raw: bigint; base36: string; elapsed: bigint; rand: number } {13 const clean = b36.trim().toLowerCase();14 let raw = 0n;15 for (const ch of clean) {16 const c = ch.charCodeAt(0);17 raw = raw * 36n + BigInt(c >= 48 && c <= 57 ? c - 48 : c - 87);18 }19 return { raw, base36: raw.toString(36), elapsed: raw / 10000n, rand: Number(raw % 10000n) };20}21 22export function generateSecret(): string {23 const bytes = crypto.getRandomValues(new Uint8Array(32));24 let num = 0n;25 for (const b of bytes) num = (num << 8n) | BigInt(b);26 let res = "";27 while (num > 0n) {28 res = B62[Number(num % 62n)] + res;29 num /= 62n;30 }31 return res.padStart(43, "0");32}
1import re, secrets, time2 3EPOCH = 1790022600 # 2026-09-22 00:00:00 UTC+3:30 (Day 0)4B36 = "0123456789abcdefghijklmnopqrstuvwxyz"5B62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"6API_TOKEN_REGEX = re.compile(r"^[0-9a-z]{6,12}:[0-9a-zA-Z]{43}$")7 8def encode_base36(n: int) -> str:9 res = []10 while n > 0:11 n, r = divmod(n, 36)12 res.append(B36[r])13 return "".join(reversed(res)) or "0"14 15def generate_muid() -> tuple[int, str]:16 elapsed = int(time.time()) - EPOCH17 raw = elapsed * 10000 + secrets.randbelow(10000)18 return raw, encode_base36(raw)19 20def parse_muid(b36: str) -> dict:21 raw = int(b36.strip(), 36)22 return {"raw": raw, "base36": encode_base36(raw), "elapsed": raw // 10000, "rand": raw % 10000}23 24def generate_secret() -> str:25 n = int.from_bytes(secrets.token_bytes(32), "big")26 res = []27 while n > 0:28 n, r = divmod(n, 62)29 res.append(B62[r])30 return "".join(reversed(res)).zfill(43)
1<?php2const EPOCH = 1790022600; // 2026-09-22 00:00:00 UTC+3:30 (Day 0)3const B62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";4const API_TOKEN_REGEX = '/^[0-9a-z]{6,12}:[0-9a-zA-Z]{43}$/';5 6function generateMuid(): array {7 $raw = (time() - EPOCH) * 10000 + random_int(0, 9999);8 return ['raw' => $raw, 'base36' => base_convert((string)$raw, 10, 36)];9}10 11function parseMuid(string $b36): array {12 $raw = (int)base_convert(strtolower(trim($b36)), 36, 10);13 return ['raw' => $raw, 'base36' => strtolower(trim($b36)), 'elapsed' => intdiv($raw, 10000), 'rand' => $raw % 10000];14}15 16function generateSecret(): string {17 $bytes = array_values(unpack("C*", random_bytes(32)));18 $res = "";19 while (true) {20 $zero = true; $rem = 0;21 for ($i = 0; $i < 32; $i++) {22 $cur = $rem * 256 + $bytes[$i];23 $bytes[$i] = intdiv($cur, 62);24 $rem = $cur % 62;25 if ($bytes[$i] !== 0) $zero = false;26 }27 $res = B62[$rem] . $res;28 if ($zero) break;29 }30 return str_pad($res, 43, '0', STR_PAD_LEFT);31}
1#!/bin/sh2# MUID & Secret generator in pure POSIX sh3EPOCH=1790022600 # 2026-09-22 00:00:00 UTC+3:30 (Day 0)4B36="0123456789abcdefghijklmnopqrstuvwxyz"5B62="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"6 7generate_muid() {8 now=$(date +%s)9 rand=$(od -An -N2 -tu2 /dev/urandom | tr -d " \t\n")10 rand=$(( rand % 10000 ))11 raw=$(( (now - EPOCH) * 10000 + rand ))12 13 # Convert 64-bit integer to Base3614 printf "%s %s\n" "$raw" "$B36" | awk "{15 n = \$1; b36 = \$2; res = \"\"16 while (n > 0) {17 rem = n % 3618 res = substr(b36, rem + 1, 1) res19 n = int(n / 36)20 }21 printf \"%s\", res22 }"23}24 25generate_secret() {26 # 32 CSPRNG bytes to 43 Base62 characters27 od -An -v -tu1 -N32 /dev/urandom | awk -v b62="$B62" "{28 for (i = 1; i <= NF; i++) bytes[n++] = \$i29 }30 END {31 res = \"\"32 while (1) {33 all_zero = 1; rem = 034 for (i = 0; i < 32; i++) {35 cur = rem * 256 + bytes[i]36 bytes[i] = int(cur / 62)37 rem = cur % 6238 if (bytes[i] != 0) all_zero = 039 }40 res = substr(b62, rem + 1, 1) res41 if (all_zero) break42 }43 while (length(res) < 43) res = \"0\" res44 printf \"%s\", res45 }"46}47 48printf "%s:%s\n" "$(generate_muid)" "$(generate_secret)"