پرش به مطلب اصلی

AuthenticationClient

AuthenticationClient بخش frontend پروتکل دیتابیس احراز هویت WaterWall است. این node روی worker شماره 0 یک line کنترلی داخلی می‌سازد و آن را در جهت upstream به node next می‌فرستد. سپس در AuthenticationServer احراز هویت می‌شود، جدول مرجع کاربران را می‌گیرد و با ارسال دوره‌ای پیام‌ها، session را زنده نگه می‌دارد و شمارنده‌های ترافیک محلی را به سرور می‌فرستد.

AuthenticationClient تونلی برای عبور داده نیست. node‌هایی که ترافیک کاربران را سرویس می‌دهند، از API داخلی آن برای خواندن اطلاعات کاربران و ثبت مصرف محلی در جدول دریافت‌شده استفاده می‌کنند.

پذیرش از cache محلی​

پس از نخستین همگام‌سازی معتبر جدول و مبنای ساعت، authenticationclientCanAuthenticate() در قطع شبکه و احراز هویت دوباره از آخرین cache حافظه استفاده می‌کند؛ cache مهلت کهنگی ندارد. enabled، انقضا، سهمیه ترافیک و reservation محلی اتصال/IP همچنان اعمال می‌شوند. نبود cache، توقف، خطای نهایی continuity یا رد صریح اعتبارنامه خود کلاینت، پذیرش جدید را می‌بندد. فقط پاسخ موفق و معتبر Session رد صریح را پاک می‌کند؛ timeout، قطع اتصال، پاسخ خراب و authentication-required چنین اثری ندارند. اتصال‌های قبلاً مجاز به ثبت مصرف و اجرای محدودیت‌های محلی ادامه می‌دهند. هر درخواست HTTP روی اتصال reused پذیرش تازه است؛ exchange جاری و CONNECT ادامه می‌یابند. ویرایش مدیر پس از refresh معتبر می‌رسد. دو کلاینت قطع‌شده با مصرف شناخته‌شده ۸۰ از سهمیه ۱۰۰ گیگابایت می‌توانند هر کدام ۲۰ مصرف کنند و مجموع نهایی ۱۲۰ شود؛ سهمیه و reservation محلی، تضمین سراسری نیستند.

ساعت و اولین استفاده محلی​

پاسخ هر صفحه باید هر دو فیلد canonical و دقیق server-time-ms و accounting-time-ms مثبت را داشته باشد؛ metadata نامعتبر refresh را بدون جایگزینی cache معتبر رد می‌کند و fallback به ساعت دیواری کلاینت وجود ندارد. سرور یک بار C0 = max(wall-ms, 1) و monotonic M0 را ثبت می‌کند و با جمع saturating زمان سپری‌شده monotonic ساعت حسابداری را پیش می‌برد. اصلاح بعدی ساعت دیواری آن را reset نمی‌کند و anchor در فایل ذخیره نمی‌شود.

اولین upload یا download واقعی غیرصفر، حتی با هزینهٔ گرد‌شدهٔ صفر، درون قفل write کاربر، نمونه تازه monotonic، origin محلی معتبر حتی در صفر و برآورد مثبت و تغییرناپذیر اولین استفاده برای گزارش را ثبت می‌کند. login، reservation، فراخوانی صفر بایت و rebase شروع مصرف نیستند. ثبت مشاهده، شمارنده و تصمیم انقضا در transaction با ترتیب user سپس stats انجام می‌شود؛ نبود خروجی notification مانع مشاهده نیست. سن سپری‌شده حتی با مدت صفر حفظ می‌شود؛ ACK، reconnect، pull تکراری یا نبود فیلد canonical آن را پاک یا جوان نمی‌کنند. مشاهده زودتر کلاینت دیگر می‌تواند سن را بیشتر کند. origin واردشده در آینده تا رسیدن زمانش آینده می‌ماند؛ مصرف محلی واقعی می‌تواند origin زودتری بسازد.

انقضای تاریخ مطلق با ساعت دیواری سرور و مدت پس از اولین استفاده با سن monotonic جدا محاسبه می‌شوند. تاریخ بدون تغییر deadline زودتر را حفظ می‌کند؛ ویرایش/پاک‌کردن هر سیاست فقط جزء خودش را دوباره محاسبه می‌کند. deadline زودتر حاکم است؛ انقضای گذشته در monotonic صفر هرگز به unlimited تبدیل نمی‌شود. state زمان/مشاهده/ACK و reservation با ID پایدار، حتی هنگام تغییر credential همان ID، منتقل می‌شوند؛ policy و timestamp مرجع جدید حفظ می‌شوند. کپی عمومی و JSON این state محلی را ندارند. تبدیل بین ماشین‌ها برآوردی با خطای تأخیر شبکه/serialization و drift است. رفتار suspend ساعت runtime عوض نمی‌شود؛ CLOCK_MONOTONIC لینوکس زمان تعلیق میزبان را حساب نمی‌کند.

جایگاه رایج​

AuthenticationClient -> ... -> AuthenticationServer

برای اتصال به سرور راه دور، chain سمت کلاینت معمولاً چنین شکلی دارد:

AuthenticationClient -> TcpConnector

مسیر connector باید در سمت سرور به chain‌ای برسد که به AuthenticationServer ختم می‌شود.

این نود چه می‌کند؟​

  • روی worker شماره 0 یک line کنترلی داخلی و بلندمدت می‌سازد.
  • با مشخصات کلاینت که در config آمده، درخواست Authenticate را به AuthenticationServer می‌فرستد.
  • session token 64 بایتی بازگشتی را ذخیره می‌کند.
  • پس از احراز هویت، جدول کامل کاربران را با GetUsersPage می‌گیرد.
  • یک نسخه محلی و محافظت‌شده با lock از جدول کاربران را برای تونل‌های دیگر نگه می‌دارد.
  • شمارنده‌های ترافیک محلی که هنوز sync نشده‌اند را هنگام جایگزینی کامل جدول حفظ می‌کند.
  • به‌صورت دوره‌ای درخواست Ping همراه با احراز هویت می‌فرستد.
  • درخواست‌های دوره‌ای PushUserStats را همراه با اطلاعات شمارنده‌های ترافیک می‌فرستد.
  • درخواست‌های GetUsersPage را به‌صورت دوره‌ای یا بر اساس تغییر revision می‌فرستد.
  • اگر transport در جهت downstream بسته شود، دوباره متصل می‌شود و احراز هویت می‌کند.
  • helper‌های جست‌وجوی کاربر، خروجی JSON، ثبت مصرف ترافیک و admission را از طریق AuthenticationClient/interface.h در اختیار بخش‌های دیگر می‌گذارد.

از آنجا که line کنترلی را خودش می‌سازد، مسئول آزاد کردن آن نیز هست.

نمونه تنظیم​

نمونه حداقلی برای احراز هویت درون همان process:

{
"name": "auth-client",
"type": "AuthenticationClient",
"settings": {
"name": "edge-1",
"secret": "long-random-secret"
},
"next": "auth-db"
}

نمونه transport محلی loopback:

{
"name": "auth-client",
"type": "AuthenticationClient",
"settings": {
"name": "edge-1",
"secret": "long-random-secret",
"ping-interval-ms": 60000,
"pull-interval-ms": 300000,
"push-interval-ms": 300000,
"reconnect-interval-ms": 5000,
"request-timeout-ms": 120000,
"max-pending-requests": 64,
"traffic-multiplier": 1,
"verbose": false
},
"next": "auth-server-connector"
}
{
"name": "auth-server-connector",
"type": "TcpConnector",
"settings": {
"address": "127.0.0.1",
"port": 9000,
"nodelay": true
}
}

مقادیر settings.name و settings.secret باید با یکی از ورودی‌های settings.auth-clients در AuthenticationServer یکسان باشند:

{
"name": "edge-1",
"secret": "long-random-secret",
"allow-user-pull": true,
"allow-stats-push": true,
"allow-user-write": false
}

فیلدهای اجباری​

فیلدهای سطح اصلی:

فیلدنوعتوضیح
namestringنام انتخابی node. node‌های دیگر با این نام به auth client ارجاع می‌دهند.
typestringباید دقیقاً "AuthenticationClient" باشد.
nextstringاجباری. node بعدی باید به یک AuthenticationServer منتهی شود.
settingsobjectمشخصات احراز هویت و تنظیمات timer‌ها.

فیلدهای اجباری داخل settings:

فیلدنوعتوضیح
namenon-empty stringنام کلاینت control-plane که برای AuthenticationServer فرستاده می‌شود.
secretnon-empty stringsecret مربوط به control-plane که همراه settings.name فرستاده می‌شود.

name و secret مربوط به control-plane هستند و مشخصات ورود کاربران ترافیک به شمار نمی‌آیند.

تنظیمات اختیاری​

گزینهنوعپیش‌فرضتوضیح
ping-interval-msعدد صحیح نامنفی60000Ping همراه با احراز هویت می‌فرستد. اگر اتصال برقرار باشد ولی احراز هویت انجام نشده باشد، Authenticate را نیز دوباره امتحان می‌کند.
pull-interval-msعدد صحیح نامنفی300000فاصله ارسال دوره‌ای GetUsersPage. مقدار 0 دریافت‌های مبتنی بر timer را غیرفعال می‌کند.
push-interval-msعدد صحیح نامنفی300000فاصله ارسال دوره‌ای PushUserStats. مقدار 0 این ارسال‌های دوره‌ای را غیرفعال می‌کند.
reconnect-interval-msعدد صحیح نامنفی5000مدت انتظار پیش از اتصال دوباره، بعد از بسته شدن transport کنترلی.
request-timeout-msعدد صحیح نامنفی120000اگر یک درخواست pending بیش از این مدت بی‌پاسخ بماند، اتصال از نو برقرار می‌شود. مقدار 0 بررسی timeout درخواست‌ها را غیرفعال می‌کند.
max-pending-requestsعدد صحیح مثبت64بیشترین تعداد درخواست‌های هم‌زمانِ در حال پردازش در پروتکل.
traffic-multiplierعدد JSON1ضریب مصرف هر دو جهت، از 0.001 تا 1000 با حداکثر سه رقم اعشار مؤثر؛ فقط هنگام راه‌اندازی.
verbosebooleanfalseلاگ‌های debug مربوط به line کنترلی، timer‌ها، درخواست‌ها و پاسخ‌ها را فعال می‌کند.

هر پنج گزینهٔ بازهٔ زمانی، عدد صحیح JSON از 0 تا 2147483647 (INT_MAX) می‌پذیرند. بازهٔ max-pending-requests از 1 تا 2147483647 است. پیش‌فرض فقط وقتی اعمال می‌شود که گزینه وجود نداشته باشد. مقدار اعشاری، نگارش دارای ممیز یا توان، رشته، null و مقدار خارج از بازه در راه‌اندازی رد می‌شوند؛ مقدارها گرد یا سرریز نمی‌شوند. معنای قبلی مقدار صریح 0 برای هر بازهٔ زمانی حفظ می‌شود. گزینه‌های شناخته‌شدهٔ تکراری رد می‌شوند و verbose فقط Boolean می‌پذیرد. رد اعتبارنامه حتی با reconnect صفر، حداقل یک ثانیه فاصله و backoff نمایی دارد.

مقدار صفر pull-interval-ms فقط cadence دوره‌ای را غیرفعال می‌کند. پس از احراز هویت موفق و هر ACK مصرف مطابق، حتی ACK مربوط به replay، refresh درخواست می‌شود؛ اگر دریافت تنظیمات شکست بخورد، گزارش مصرف از baseline قبلی میان تلاش‌های بعدی ادامه دارد. درخواست صریح pull نیز مجاز است.

push-interval-ms فقط ارسال‌های دوره‌ای را غیرفعال می‌کند. تونل‌های دیگر همچنان می‌توانند از طریق API داخلی درخواست ارسال آمار بدهند.

استفاده از سایر نودها​

معمولاً node‌های دیگر با نام node به AuthenticationClient ارجاع می‌دهند. برای نمونه، Socks5Server می‌تواند به شکل زیر از یک AuthenticationClient موجود استفاده کند:

{
"name": "socks-server",
"type": "Socks5Server",
"settings": {
"auth-client-node-name": "auth-client",
"connect": true,
"udp": false
},
"next": "outbound-tcp"
}

AuthenticationClient در مسیر داده Socks5Server قرار نمی‌گیرد. این node اتصال کنترلی جداگانه‌ای دارد و جدول کاربران دریافت‌شده را از طریق API داخلی در اختیار node‌های دیگر می‌گذارد.

مدل ارتباطی​

کلاینت یک line کنترلی تحت مالکیت خود روی worker صفر دارد. Init state را پیش از callback بعدی منتشر می‌کند و Est باعث ارسال Authenticate با token صفر می‌شود. درخواست‌ها upstream و پاسخ‌ها downstream هستند؛ هر درخواست correlation مستقل دارد. Session موفق شامل token و epoch است: ۶۴ بایت hexadecimal برای هر کدام، مجموع ۱۲۸ بایت. Authenticate نخست name و secret دارد؛ reconnect فیلد epoch را نیز برای resume می‌فرستد.

قطع transport، token و درخواست‌های موقت را پاک می‌کند اما batch مصرف حل‌نشده و snapshot آن باقی می‌مانند. ترتیب reconnect: authenticate/resume، replay همان batch، ACK معتبر و سپس GetUsersPage. پاسخ قدیمی نمی‌تواند epoch مستقرشده را عوض کند. درخواست‌های pull/push هنگام قطع، Pause یا تداخل عملیات برای اجرای بعدی ادغام می‌شوند. پس از احراز هویت یا ACK مصرف، دریافت کامل و الزامی جدول باید پیش از ساخت batch جدید تمام شود؛ در reconnect، ابتدا batch باقی‌مانده replay و تأیید می‌شود. پس از پایان این pull الزامی، push منتظر بر pullهای عادی دوره‌ای یا صریح اولویت دارد تا درخواست‌های مکرر pull ارسال مصرف را برای همیشه عقب نیندازند. اگر مصرف تازه‌ای برای ارسال نباشد، pull عادیِ منتظر می‌تواند بلافاصله اجرا شود.

وقتی جدول pending به max-pending-requests می‌رسد، pull، push و catch-up مطلوب حفظ می‌شوند و تا آزادشدن ظرفیت منتظر می‌مانند؛ batch دوباره ساخته نمی‌شود و warning یا timer کوتاهِ تکراری ایجاد نمی‌شود. پاسخ مطابق باید ابتدا از نظر معنا بررسی و enrollment، ACK یا خطای آن پردازش شود، سپس کار منتظر ادامه می‌یابد. rollback محلی ارسال، ادامه‌ای برای turn بعدی می‌گذارد؛ correlation قدیمی ظرفیتی آزاد نمی‌کند. با timeout و intervalهای اختیاری صفر، انتظار نامحدود برای پاسخ بدون مصرف پیوستهٔ CPU مجاز است.

tick دوره‌ای در traversal جاری ادغام می‌شود. درخواست صریح authenticationclientRequestPull() در همان زمان، یک traversal دیگر را برای بعد نگه می‌دارد؛ چند درخواست صریح در یک traversal اضافی ادغام می‌شوند و نصب جدول این تقاضا را پاک نمی‌کند.

Timerها​

همه timerها روی worker صفر هستند. ping و sync اختیاری‌اند. request_timer مستقل، نزدیک‌ترین deadline درخواست را پیش از Payload منتشر می‌کند و حتی با خاموش بودن ping/pull/push timeout را اجرا می‌کند؛ timeout صفر آن را خاموش می‌کند. پاسخ فقط deadline درخواست خودش را حذف می‌کند و محاسبات در یک ساعت monotonic هستند.

retry مثبت اولین deadline را حفظ می‌کند و tickهای ping آن را عقب نمی‌اندازند. callback زمان واقعی due را دوباره بررسی می‌کند. interval صفر یک کار در صف برای turn بعدی loop است، نه فراخوانی بازگشتی یا timer صفر. latch پیش از queue submission منتشر می‌شود؛ run یا cancellation آن را یک بار تسویه می‌کند. cancellation به line، pool یا timer worker دست نمی‌زند. timerها در onWorkerQuiesce جدا می‌شوند و onWorkerStop line تحت مالکیت را تخلیه می‌کند.

ضریب مصرف ترافیک​

گزینهٔ اختیاری traffic-multiplier داخل settings یک عدد JSON است و پیش‌فرض آن 1 است. مقدارهای 0.001 تا 1000 با حداکثر سه رقم اعشار مؤثر پذیرفته می‌شوند؛ مثلاً 2 برای یک گیگابایت ترافیک واقعی، دو گیگابایت از سهمیه کم می‌کند، 1.5 یک‌ونیم گیگابایت و 0.5 نیم گیگابایت. صفر، مقدار منفی، رشته و دقت پشتیبانی‌نشده هنگام راه‌اندازی رد می‌شوند. ضریب تا پایان عمر همین instance ثابت است.

ضریب برای upload و download همهٔ کاربرها و پروتکل‌هایی که این instance را استفاده می‌کنند یکسان است. UserController و دیگر callerها بایت واقعی اندازه‌گیری‌شده را بدون ضرب اولیه به AuthenticationClient می‌دهند. کلاینت هر اندازه‌گیری تازه را دقیقاً یک بار، پیش از افزایش شمارنده و بررسی سهمیه، به مصرف قابل‌کسر تبدیل می‌کند؛ در حالت آفلاین نیز همین قاعده برقرار است. مجموع دریافتی از سرور، batch معلق، retry، ACK و بدهی catch-up قبلاً با واحد قابل‌کسر هستند و هرگز دوباره ضرب نمی‌شوند. AuthenticationServer مقدار گزارش‌شده را بدون ضریب دیگری جمع می‌کند.

محاسبه با عدد صحیح انجام می‌شود و هر کاربر برای هر جهت باقی‌ماندهٔ کسری مستقل دارد. فقط بایت کامل قابل‌کسر به شمارنده اضافه می‌شود؛ خردکردن ترافیک به payload کوچک هزینه را عوض نمی‌کند. با 0.5، دو upload یک‌بایتی مجموعاً یک بایت مصرف حساب می‌شوند؛ download یک‌بایتی باقی‌ماندهٔ نیم‌بایت مستقل دارد. باقی‌مانده‌ها در refresh با ID پایدار، حتی با تغییر credential همان حساب، منتقل و هنگام reconnect حفظ می‌شوند. این state فقط در RAM است، در JSON دیتابیس یا پروتکل ذخیره نمی‌شود و با حذف کاربر یا نابودی کلاینت از بین می‌رود؛ crash recovery وجود ندارد. سرریز شمارنده به مقدار کوچک‌تر برنمی‌گردد و با اشباع کنترل می‌شود.

اولین ترافیک واقعی مثبت، شمارش زمان انقضا را شروع می‌کند، حتی اگر ضریب تخفیفی هنوز یک بایت کامل هزینه نسازد. login و فراخوانی صفر بایت شروع استفاده نیستند. مجموع ترافیک حساب، محدودیت ترافیک و بایت تأییدنشدهٔ status همگی بایت قابل‌کسر از سهمیه هستند، نه الزاماً بایت فیزیکی شبکه. مجموع کلاینت‌های با ضریب متفاوت، ترافیک واقعی را قابل بازیابی نمی‌کند. bandwidth، سرعت، تعداد اتصال/IP و timestamp ضرب نمی‌شوند. برای نرخ‌های مختلف از instanceهای AuthenticationClient جدا با هویت producer مستقل استفاده کنید.

جدول کاربران محلی​

کلاینت یک جدول محلی فعال از کاربران نگه می‌دارد که یک wrwlock_t در سطح تونل از آن محافظت می‌کند.

از دید تونل‌های دیگر، جایگزینی جدول در جریان GetUsersPage به‌صورت atomic انجام می‌شود:

  1. پاسخ JSON خوانده و به یک users_t تازه تبدیل می‌شود.
  2. جدول تازه اعتبارسنجی می‌شود؛ از جمله اینکه range‌های هم‌پوشان در wireguard-allowed-ips پذیرفته نمی‌شوند.
  3. deltaهای مصرف قابل‌کسرِ sync‌نشده، باقی‌ماندهٔ کسری هر جهت و شمارنده‌های runtime مخصوص همان process، با durable user id منتقل می‌شوند؛ هیچ fallback از credential برای هویت کاربر وجود ندارد.
  4. deadline محلی انقضای هر کاربر از فیلدهای زمانی متعلق به سرور و هر دو metadata ساعت، با حفظ سن اولین استفاده و جزء تاریخ مطلق محاسبه می‌شود.
  5. pointer جدول فعال زیر write lock مربوط به rwlock جابه‌جا می‌شود.
  6. شمارنده generation افزایش می‌یابد.
  7. جدول قدیمی پس از جابه‌جایی آزاد می‌شود.

reader‌ها هنگام کپی JSON یا آمار و نیز هنگام به‌روزرسانی ترافیک با ID پایدار، read lock همان rwlock را نگه می‌دارند. به این ترتیب هیچ pointer‌ای داخل users_t فعلی در میانه عملیات نامعتبر نمی‌شود.

جدول محلی هم نقش cache را دارد و هم شمارنده‌های محلی را تجمیع می‌کند. مرجع اصلی config کامل هر کاربر، از جمله wireguard-allowed-ips اختیاری و first-usage-at-ms، سرور است.

API داخلی​

تونل‌های دیگر این header را include می‌کنند:

#include "AuthenticationClient/interface.h"

این API داخلی WaterWall با API خارجی تونل در api.c تفاوت دارد.

تونل‌های دیگر نباید pointer خام user_t * را از AuthenticationClient بگیرند یا cache کنند. در عوض باید از user_handle_t استفاده کنند؛ شناسه‌ای مقداری که شامل موارد زیر است:

sha256(password)
users_generation
user_id

user_id از رکورد sync‌شده کپی می‌شود و handle معتبر به ID غیرصفر نیاز دارد. helperهای enforcement، ID پایدار را همراه کف generation دورهٔ عضویت بررسی می‌کنند؛ refresh معمولی عضویت را حفظ می‌کند ولی حذف و ساخت دوبارهٔ همان ID، handle قدیمی را زنده نمی‌کند. helperهای snapshot قدیمی ممکن است generation دقیق جدول جاری را بخواهند.

helper‌های رایج وضعیت:

authenticationclientGetState(t)
authenticationclientIsReady(t)
authenticationclientCanAuthenticate(t)
authenticationclientUsersGeneration(t)

helper‌های جست‌وجو:

authenticationclientGetUserByPassword(t, password, &handle)
authenticationclientGetUserByPasswordWithResult(t, password, &handle)
authenticationclientGetUserBySHA224(t, sha224, &handle)
authenticationclientGetUserBySHA256(t, sha256, &handle)
authenticationclientGetUserByUUID(t, uuid, &handle)
authenticationclientGetUserByWireGuardPublicKey(t, publickey, &handle)
authenticationclientGetUserByPasswordWithProfile(t, password, &handle, &profile)
authenticationclientGetUserBySHA224WithProfile(t, sha224, &handle, &profile)
authenticationclientGetUserByUUIDWithProfile(t, uuid, &handle, &profile)
authenticationclientGetUserByWireGuardPublicKeyWithProfile(t, publickey, &handle, &profile)
authenticationclientUserProfileClear(&profile)

helper‌های کپی خروجی و آمار:

authenticationclientUserHandleIsLive(t, &handle)
authenticationclientUserToJson(t, &handle)
authenticationclientUsersToJson(t)
authenticationclientUserGetStats(t, &handle, &stats)
authenticationclientUserAddTraffic(t, &handle, upload, download)

helper‌های اعمال محدودیت در runtime:

authenticationclientUserTryAdmitConnection(t, &handle, &ip_key, now_ms)
authenticationclientUserReleaseConnection(t, &handle, &ip_key)
authenticationclientUserAccountTraffic(t, &handle, upload, download, now_ms)
authenticationclientUserShouldClose(t, &handle, now_ms)

helper‌های همگام‌سازی دستی:

authenticationclientRequestPull(t)
authenticationclientRequestPush(t)

authenticationclientUserToJson() و authenticationclientUsersToJson() آبجکت‌های تازه cJSON برمی‌گردانند و caller مالک آن‌هاست. آمار بر حسب مصرف قابل‌کسر در فضای ذخیره‌سازی caller کپی می‌شود. هر دو helper ترافیک، بایت واقعی اندازه‌گیری‌شده را می‌گیرند؛ caller نباید پیشاپیش آن را ضرب کند. node‌هایی که ترافیک را سرویس می‌دهند نباید user_t را مستقیم تغییر دهند.

آرگومان now_ms در helper‌های اعمال محدودیت، بر پایه ساعت monotonic محلی کلاینت است. هنگام جایگزینی جدول با نتیجه GetUsersPage، فیلدهای انقضای متعلق به سرور به همین مبنای زمانی تبدیل می‌شوند.

session-unavailable خطای موقت ظرفیت سرور یا تولید token/epoch پس از تطبیق credential است. مسیر عمومی RemoteError با backoff محدود دوباره متصل می‌شود؛ cache قابل‌استفاده، admission و حسابداری معلق حفظ می‌شوند. فقط authentication-failed مرتبط با Authenticate جاری، رد صریح credential را ثبت می‌کند.

خروجی‌های کامل authenticationclientUserToJson() و authenticationclientUsersToJson() دارای credential، از جمله رمز plaintext، و مخصوص caller مورداعتمادند؛ caller باید درخت برگشتی را حذف کند. خروجی کل جدول زیر قفل‌های read جدول و collection برای همهٔ حساب‌ها تخصیص و کپی دارد؛ وضعیت عمومی از projectionهای redacted موجود استفاده کند.

حالت‌های Client​

authenticationclientGetState() برمی‌گرداند:

حالتمعنی
kAuthenticationClientStateStoppedکلاینت متوقف است یا در دسترس نیست.
kAuthenticationClientStateConnectingکلاینت راه افتاده، اما هنوز session کنترلی با احراز هویت موفق ندارد.
kAuthenticationClientStateAuthenticatingtransport متصل است یا token وجود دارد، اما جدول کامل کاربران هنوز بارگذاری نشده است.
kAuthenticationClientStateContinuityErrorپیوستگی حسابداری از دست رفته و رسیدگی اپراتور لازم است.
kAuthenticationClientStateReadyکلاینت احراز هویت شده و جدول کاربران را بارگذاری کرده است.

authenticationclientIsReady() فقط در kAuthenticationClientStateReady مقدار true دارد.

جست‌وجوی password که نتیجه تفصیلی می‌دهد، ممکن است یکی از این مقادیر را برگرداند:

نتیجهمعنی
oklookup موفق بود.
invalid password lookupآرگومان نامعتبر، password خالی یا نبودن handle خروجی.
password hash failedمحاسبه SHA-256 شکست خورد.
users table unavailableجدول محلی کاربران هنوز بارگذاری نشده است.
user not foundکاربری با این password پیدا نشد.
password mismatchhash منطبق بود، اما بررسی password به‌صورت plaintext موفق نشد.
user disabledکاربر پیدا شده غیرفعال است.
user expiredکاربر پیدا شده بر اساس ساعت محلی کلاینت منقضی شده است.
user limit reachedکاربر پیدا شده به یکی از محدودیت‌های تعیین‌شده رسیده است.

پروتکل Wire​

پروتکل با AuthenticationServer تطابق دارد.

پیام request:

u32 body_len
u8[64] session_token
request_frame

فریم request:

u8 request_type
u32 correlation_id
u32 request_data_len
u8[request_data_len] request_data

پیام response:

u32 body_len
u64 config_revision
u64 stats_revision
response_frame...

فریم response:

u8 response_type
u32 correlation_id
u32 response_data_len
u8[response_data_len] response_data

همه فیلدهای integer با ترتیب big-endian نوشته می‌شوند. کلاینت در هر پیام یک فریم درخواست می‌فرستد. parser می‌تواند هر تعداد فریم پاسخ را در یک پیام پاسخ بخواند و هر فریم را با correlation ID به درخواستش مرتبط کند.

revision‌های پاسخ به‌عنوان آخرین revision‌های سرور ذخیره می‌شوند و پیش از دریافت‌های زمان‌بندی‌شده، با revision جدول کاربران محلی مقایسه می‌شوند.

محدودیت‌ها و transport محافظت‌شده​

پروتکل credential کلاینت و جدول کامل شامل رمز کاربران را حمل می‌کند. اتصال راه دور غیرقابل‌اعتماد باید transport رمزشده با احراز هویت سرور و بررسی هویت داشته باشد. مثال 127.0.0.1 فقط محلی است. chain محافظت‌شده: AuthenticationClient -> TlsClient -> TcpConnector در برابر TcpListener -> TlsServer -> AuthenticationServer. برای TlsClient از settings: {"sni":"auth.example.com","verify":true,"alpns":[]} استفاده کنید؛ verify:true زنجیره گواهی و hostname را بررسی می‌کند. سمت سرور cert-file و key-file نود TlsServer باید گواهی معتبر همان نام را تعیین کنند. credential تنظیم‌شده پس از JSON escaping و با epoch رزومه ۶۴ بایتی باید در payload داده Authenticate به اندازه ۱۶۳۷۵ بایت جا شود؛ مقدار بزرگ‌تر هنگام startup رد می‌شود و truncate نمی‌شود.

جدول کامل با صفحه‌های محدود و validation نهایی همگام می‌شود؛ محدودیت ۱۶ MiB روی یک پیام باقی است، نه کل جدول.

رد stats-push-not-allowed پذیرش از cache را با همان enabled/expiry/quota و ظرفیت محلی حفظ می‌کند. batch دقیق، بدهی و شواهد اولین استفاده باقی می‌مانند. خطاهای push غیرنهایی مانند invalid-usage-batch، usage-digest-failed یا خطای ناشناخته نیز بدون فرض درباره commit، همان batch را حفظ می‌کنند. reconnect و reauthenticate پس از cooldown monotonic حداقل ۳۰ ثانیه یا interval اتصال مجدد در صورت بزرگ‌تر بودن انجام می‌شود؛ حتی با همه intervalهای صفر، retry سر موعد مستقل زمان‌بندی می‌شود. ترافیک، درخواست صریح یا Resume آن را دور نمی‌زنند. Session موفق این وضعیت را پاک نمی‌کند؛ فقط ACK مطابق batch حفظ‌شده آن را پاک می‌کند. status دلیل محدود stats_push_denied یا stats_push_rejected و deadline/debt واقعی را نشان می‌دهد. diagnostic خطای سرور حداکثر ۱۲۸ بایت ورودی را escape می‌کند و truncation را مشخص می‌کند.

تا حل batch هیچ pull از آن جلو نمی‌زند؛ cache مانند outage قدیمی می‌ماند و ترافیک بعدی جدا جمع می‌شود. بازیابی به همان epoch زنده سرور وابسته است؛ permission تنظیم startup است و تغییر آن همراه restart تضمین بازیابی checkpoint حافظه را ندارد. usage-replay-conflict و usage-sequence-invalid خطای نهایی‌اند و پذیرش تازه را می‌بندند. authentication-required همان مسیر عادی reauthentication را با حفظ batch طی می‌کند.

هنگام pull الزامی یا in-flight، first-use push با latch کلی و flag هر user معلق می‌ماند. اتمام pull بدون packet دیگر push را ادامه می‌دهد. Pause نوشتن timer پیشرفت تکراری ایجاد نمی‌کند؛ Resume کار ثبت‌شده را ادامه می‌دهد.

Stats Push​

دلتاهای بایت و مشاهده اولین مصرف محلی با ID پایدار همگام می‌شوند:

{"epoch":"<64 hexadecimal bytes>","sequence":1,"users":[{"id":42,"up":10,"down":0,"first-usage-at-ms":1792252800000}]}

ریشه فقط epoch، sequence، users و هر ورودی دقیقاً id، up، down، first-usage-at-ms دارد؛ تمام فیلدها اجباری‌اند. کلید ناشناخته/تکراری یا غیرترافیکی، NUL خام/escaped، UTF-8 نامعتبر، داده اضافی، عمق بیش از سه container یا بیش از ۳۶۸۷۱ مقدار واژگانی (شامل کلیدها) رد می‌شود و بودجه پیام authentication برقرار است. کلاینت یک batch immutable و snapshot آن را تا ACK دارای epoch، sequence و SHA-256 digest مطابق یا اثبات معتبر عدم commit نگه می‌دارد. refusal، timeout، پاسخ malformed و قطع اتصال آن را حذف نمی‌کنند. retry همان بایت‌ها را با correlation تازه و token فعلی می‌فرستد. batch خالی ارسال نمی‌شود، هر batch حداکثر ۴۰۹۶ ورودی دارد و sequence از ۱ بدون wrap جلو می‌رود. فقط کاربران/directionهای subset تأییدشده baseline را جلو می‌برند. اعداد صحیح ساده تا INT64_MAX و رشته decimal تا UINT64_MAX دقیق هستند؛ tokenهای decimal/exponent یا مقادیر کسری، منفی، nonfinite و overflow رد می‌شوند.

اگر A مجموع محلی، B baseline تأییدشده و S snapshot باشد، ACK معتبر B را برای همان subset به S می‌رساند. pull بعدی مجموع G + (A - B) را با baseline برابر G نصب می‌کند. هنگام batch حل‌نشده pull نصب نمی‌شود و push از pull در حال اجرا جلو نمی‌زند. هر ACK، حتی replay، needs-pull: true دارد تا زمان اولین استفاده سرور و مجموع دیگر کلاینت‌ها دریافت شود. شکست غیرحسابداری refresh مانع گزارش‌های تازه از baseline قبلی نیست؛ retry دریافت مستقل ادامه می‌یابد. کار اولین استفاده و درخواست‌های مسدودشده برای اجرای بعدی باقی می‌مانند.

هر timestamp گزارش، همان مشاهده تغییرناپذیر تولیدکننده است، حتی در گزارش مثبت پس از ACK. timestamp، ID و sequence غیرصفرند. برای مشاهده ACKنشده، هر دو delta می‌توانند صفر باشند؛ saturation شمارنده مانع گزارش metadata نیست. ACK فقط totals و markerهای همان snapshot را تأیید می‌کند و origin/سن را نگه می‌دارد؛ حلقه resend خالی نداریم.

پس از replay و refresh الزامی reconnect، بدهی نسبی بایت‌ها و مشاهده‌ها تا همان نقطه capture و خودکار در batchهای محدود تخلیه می‌شود؛ بعد از هر ACK یک pull درخواست می‌شود. با همه intervalهای اختیاری صفر نیز کار انجام می‌شود. پیشرفت روی worker صفر با timer غیرصفر بین batchها yield و refusal منابع را retry می‌کند. ناتوانی در ایجاد trigger الزامی، orderly shutdown می‌خواهد؛ quiescence آن را متوقف می‌کند و دوباره فعال نمی‌سازد. بدهی نسبی تا ACK مطابق حفظ می‌شود، حتی اگر rebase saturating آن را در شمارنده مرئی پنهان کند. capture محدود است و زیر ترافیک پیوسته یا rebase مصرف کلاینت دیگر پایان می‌یابد. مصرف پس از cut از سیاست عادی push و trigger اولین استفاده پیروی می‌کند. قطع بعدی هم batch تغییرناپذیر و هم بدهی نسبی باقی‌مانده catch-up را نگه می‌دارد. پس از replay/refresh، capture تازه این بدهی را با deltaهای مرئیِ تأییدنشده ادغام می‌کند، بدون اینکه بخش هم‌پوشان دوبار شمرده شود. فقط ACK مطابق، بدهی همان گزارش را مصرف می‌کند؛ قطع اتصال یا rebase شمارنده آن را پاک نمی‌کند. نابودی نهایی تونل این state را از RAM آزاد می‌کند و تحویل نهایی را تضمین نمی‌کند.

checkpoint سرور با هویت احراز‌شده مرتبط است و expiry یا جایگزینی session آن را پاک نمی‌کند. processهای مستقل نام تنظیم‌شده مستقل لازم دارند. از دست رفتن epoch با batch نامطمئن، وضعیت kAuthenticationClientStateContinuityError ایجاد می‌کند: batch حفظ می‌شود، transport بسته می‌شود و تلاش خودکار تا رفع وضعیت حسابداری توسط اپراتور متوقف می‌شود؛ API برای reset یا reconciliation وجود ندارد. بدون batch نامطمئن، از دست رفتن continuity در Authenticate اجازهٔ ثبت تازه و pull می‌دهد؛ تناقض checkpoint در صفحه همچنان نهایی است. این تضمین برای قطع اتصال در زمان اجرای دو process است و بازیابی مصرف پس از crash/restart ارائه نمی‌دهد. ACK پذیرش در حافظه سرور است، نه ذخیره دیسک. ذخیره دوره‌ای/نهایی دیتابیس checkpoint را ذخیره نمی‌کند. restart کلاینت ممکن است مصرف گزارش‌نشده را از بین ببرد؛ restart سرور ممکن است مصرف ACKشده پس از آخرین ذخیره موفق و تمام checkpointها را از دست بدهد. shutdown تضمین تحویل نهایی مصرف نیست.

ترافیک تجمعی و زمان اولین استفاده متعلق به مجموع سرورند. UpdateUser با ID پایدار فقط تنظیمات صریح را patch می‌کند؛ شمارنده، زمان ایجاد/اولین استفاده، credential و runtime حفظ می‌شوند. patch مجوز allow-user-write و batch مجوز مستقل allow-stats-push می‌خواهد. عملیات remote reset وجود ندارد. اتصال فعال و reservation IP وضعیت admission محلی‌اند، نه telemetry global. آمار ذخیره/import شده IP، device، connection و speed نمای قابل اعتماد از اشغال زنده global یا ورودی admission نیستند. گزارش آینده connection به snapshot هر producer، ترتیب و expiry، و speed به پنجره اندازه‌گیری و freshness نیاز دارد؛ مقدار مفقود صفر نیست. IP متمایز نیازمند مجموعه هویت، پنجره و حذف تکرار بین producerها است. device به تعریف هویت و روش جمع‌آوری نیاز دارد. هیچ‌یک از این گزارش‌ها یا reset در این مرحله پیاده‌سازی نشده است.

lookup فقط enabled، expiry و quota ترافیک را می‌سنجد. gaugeهای ذخیره‌شده IP، connection، device و speed ظرفیت زنده را اشغال نمی‌کنند؛ reservation واقعی IP و connection با helper اتمی runtime انجام می‌شود.

خطای مرتبط usage-accounts-missing یک proof JSON شامل epoch، sequence و digest تلاش، checkpoint-sequence، checkpoint-digest و آرایهٔ غیرخالی missing-ids دارد؛ عددها رشتهٔ ده‌دهی‌اند. کلاینت بایت‌های دقیق batch، epoch، checkpoint قبلی و عضویت تمام IDها را بررسی می‌کند. فقط پس از این اثبات عدم commit، batch آزاد می‌شود؛ sequence، baseline و بدهی مصرف نمی‌شوند. IDهای مفقود فوراً از پذیرش و forwarding جدید کنار گذاشته می‌شوند؛ ترافیک قبلاً تولیدشده هنوز ثبت و سپس اتصال بسته می‌شود و release اتصال/ticket معتبر است. کلاینت refresh می‌خواهد و می‌تواند همان sequence را برای حساب‌های باقی‌مانده بازسازی کند. proof نامنطبق یا malformed batch نامطمئن را دست‌نخورده نگه می‌دارد. orphan_debt_count تعداد تعهدات ID مفقود را نشان می‌دهد؛ حالت بیکار با فقط این بدهی orphan_debt است و timer کوتاه بیهوده ایجاد نمی‌کند. کپی حسابداری status پس از آزاد کردن mutex کنترل و زیر lock جدول انجام می‌شود.

رفتار Lifecycle و جهت‌ها​

AuthenticationClient یک node کنترلی در ابتدای chain است و درخواست‌ها را در جهت upstream می‌فرستد:

tunnelNextUpStreamInit()
tunnelNextUpStreamPayload()
tunnelNextUpStreamFinish()

پاسخ‌های سرور از طریق callback‌های downstream دریافت می‌شوند:

DownStreamEst
DownStreamPayload
DownStreamPause
DownStreamResume
DownStreamFinish

Pause و resume به تونل قبلی بازگردانده نمی‌شوند، چون پیش از AuthenticationClient تولیدکننده داده‌ای وجود ندارد. این callback‌ها فقط مشخص می‌کنند که آیا می‌توان درخواست تازه‌ای را فوراً در پروتکل فرستاد یا نه.

state مربوط به line کنترلی، stream خواندن پاسخ‌های downstream را نگه می‌دارد. این state پیش از tunnelNextUpStreamInit() مقداردهی اولیه می‌شود و قبل از انتشار finish در جهت upstream یا آزاد شدن line تحت مالکیت کلاینت، از بین می‌رود.

با دریافت finish در جهت downstream، کلاینت:

  1. pointer line کنترلی فعال را زیر mutex کنترلی پاک می‌کند.
  2. session token، وضعیت احراز هویت، flag‌های in-flight و درخواست‌های pending را پاک می‌کند.
  3. state این تونل برای line را از بین می‌برد.
  4. line کنترلی تحت مالکیت خود را آزاد می‌کند.
  5. batch حل‌نشده و بدهی باقی‌مانده catch-up را حفظ می‌کند و مگر هنگام توقف یا خطای continuity، retry را زمان‌بندی می‌کند.

وقتی کلاینت خودش line کنترلی را می‌بندد، برای مثال هنگام خاموش شدن worker شماره 0 یا پردازش یک پاسخ malformed، ابتدا state محلی line را از بین می‌برد، tunnelNextUpStreamFinish() را می‌فرستد و سپس، اگر line هنوز زنده باشد، آن را آزاد می‌کند.

نکته‌ها​

  • AuthenticationClient نیاز به node next دارد.
  • یک node control-plane است، نه node‌ای برای عبور داده.
  • فقط مالک line کنترلی داخلی است که خودش ساخته و تنها همان را آزاد می‌کند.
  • از packet line استفاده نمی‌کند و packet tunnel نیست.
  • سقف اندازه داده درخواست، payload پاسخ و payload پیام بیرونی 16 MiB است.
  • secretها، session token‌ها، passwordها و raw users JSON عمداً در verbose logging چاپ نمی‌شوند.

عملکرد پایگاه داده کاربران​

  • جست‌وجوی password متنی فقط یک بار SHA-256 را محاسبه می‌کند و بدون fallback scan از طریق index هش SHA-256 نتیجه را پیدا می‌کند؛ بنابراین هزینه متوسط hit و miss برابر O(1) است. سپس password متنی candidate به‌صورت دقیق مقایسه می‌شود تا حتی در صورت برخورد فرضی SHA-256، احراز هویت به‌شکل fail-closed انجام شود.
  • baseline همگام‌سازی محلی با deep copy بومی usersCopy() تکثیر می‌شود، نه با رفت‌وبرگشت از طریق JSON. فقط پاسخ شبکه‌ای دریافت‌شده برای GetUsersPage همچنان به یک بار parse کردن JSON در جدول فعال نیاز دارد.

متادیتای نود​

metadata برگرفته از کد منبع:

ویژگیمقدار
node flagskNodeFlagChainHead
can_have_prevfalse
can_have_nexttrue
layer_groupkNodeLayer4
layer_group_prev_nodekNodeLayerNone
layer_group_next_nodekNodeLayer4
required_padding_left0 bytes

پیام‌های کامل پاسخ پیش از بررسی سقف ورودی ناقص پردازش می‌شوند؛ بنابراین یک تحویل می‌تواند چند پیام داشته باشد. ورودی ناقص کمتر از 16 MiB + 20 bytes است و پس از فشرده‌سازی بافرها حداکثر ۱۰۲۴ entry و 32 MiB allocation charge دارد. ورودی تو‌در‌تو، همراه با ادامهٔ خوانده‌نشدهٔ قبلی، سقف یک پیام کامل به‌اضافهٔ 2 MiB را دارد؛ قبل از رد کردن ورودی، بافرهای قطعه‌قطعه ادغام می‌شوند. تحویل خالی آزاد می‌شود. یک پیام استخراج‌شده و مقصد ادغام، scratch محدود هستند؛ تحویل همگام فعلی، جدول کاربران و cache استخرها خارج از سقف ورودی نگهداری‌شده‌اند.

API وضعیت خارجی​

گره APIServer عملیات فقط‌خواندنی status را روی worker 0 در دسترس قرار می‌دهد. نوع محتوای درخواست و پاسخ application/json و کدگذاری آن UTF-8 است. بدنهٔ {} وضعیت پایه را می‌دهد؛ برای حسابداری تجمیعی از {"include_accounting":true} استفاده کنید. فیلد ناشناخته یا تکراری، مقدار غیربولی و JSON نامعتبر رد می‌شوند.

traffic_multiplier همیشه ضریب مؤثر را به صورت عدد دقیق ده‌دهی JSON نشان می‌دهد، حتی بدون نمونه‌گیری حسابداری. can_authenticate و blocking_reason مستقل از connected، authenticated و sync_phase هستند. ترتیب دلیل مسدودشدن: توقف، شروع‌نشدن، خطای پیوستگی، رد اعتبارنامه، کش/ساعت نامعتبر، سپس none. وضعیت صف و درخواست جاری، اندازه و شمار ورودی‌های batch حل‌نشده، مهلت‌های باقی‌مانده، revisionهای اعمال‌شده و مشاهده‌شده، سن آخرین موفقیت احراز هویت/نصب جدول و ساعت/ACK مصرف، و آخرین خطای محدودشده گزارش می‌شوند. retained_catchup_slots تعداد جایگاه‌های ذخیره‌شده است و شمار دقیق کاربران بدهکار نیست. وقتی مصرف ثبت‌شده یا مشاهدهٔ اولین مصرف منتظر batch بعدی است، sync_phase برابر catchup_pending است. مرحلهٔ capture، refresh اجباری و عملیات در حال اجرا وضعیت مشخص‌تر خود را حفظ می‌کنند. کلاینت با وضعیت idle ممکن است هنوز ترافیک محلی داشته باشد که منتظر push دوره‌ای بعدی است. زمان موفقیت نامعلوم null است؛ صفر ساعت monotonic معتبر است. تاریخچه با اتصال مجدد باقی می‌ماند. سن ACK پذیرش در حافظهٔ سرور را نشان می‌دهد و به معنای ذخیرهٔ پایدار روی دیسک نیست.

accounting_state یکی از not_requested، available یا unavailable است. تنها در نمونهٔ درخواستی و موفق، locally_unacknowledged_upload_bytes، locally_unacknowledged_download_bytes، usage_user_count، first_use_observation_count، accounting_lower_bound، sampling_started_ms و sampling_finished_ms وجود دارند. شمارنده‌های upload/download بر حسب بایت قابل‌کسر از سهمیه هستند. ACK گم‌شده ممکن است متعلق به مصرفی باشد که سرور قبلاً اعمال کرده است. همهٔ اعداد بدون علامت ۶۴ بیتی به صورت رشتهٔ ده‌دهی JSON ارسال می‌شوند. هیچ token، epoch، batch خام، شناسه یا اطلاعات کاربر و فهرست IP برگردانده نمی‌شود.

بدهی فعال، catch-up و batch تغییرناپذیر بر اساس شناسه ادغام می‌شوند و همپوشانی دوباره شمرده نمی‌شود؛ ترافیک بعدی و بدهی حفظ‌شدهٔ کاربران غیرفعال نیز منظور می‌شوند. شمار مشاهدهٔ اولین مصرف از وضعیت صریح تأییدنشده محاسبه می‌شود، نه صرفاً وجود timestamp در batch. اشباع شمارنده یا سرریز مجموع، حتی با مقدار نمایشی صفر، accounting_lower_bound را فعال می‌کند. نمونه‌گیری یک بازه است و ترافیک را به طور سراسری متوقف نمی‌کند. حافظه و مهلت پیش از کار و میان مراحل محدود بررسی می‌شوند؛ در صورت ناکافی‌بودن، وضعیت پایه حفظ می‌شود و حسابداری unavailable است. پرس‌وجو شمارنده‌ها، baseline، مشاهده‌ها، batch، retry یا زمان‌بندی همگام‌سازی را تغییر نمی‌دهد.

پهنای باند محلی هر user​

UserController مقدار limit.bandwidth.u و .d را بر حسب بایت منطقی واقعی در ثانیه اعمال می‌کند؛ صفر نامحدود است. همهٔ lineها، controllerها و workerهای همین AuthenticationClient سهم هر user و جهت را مشترک مصرف می‌کنند؛ userها و clientهای دیگر مستقل‌اند و هماهنگی بین ماشین‌ها وجود ندارد. ورود مثبت first-use را پیش از queue شروع می‌کند، ولی مصرف سهمیه فقط هنگام handoff ثبت می‌شود. bucket و ticket با ID پایدار در refresh منتقل و از JSON، snapshot و persistence حذف می‌شوند. multiplier فقط سهمیه را تغییر می‌دهد.

همگام‌سازی صفحه‌ای و retry​

GetUsersPage شماره ۹ و پاسخ Users شماره ۳ جدول کامل را صفحه‌ای منتقل می‌کنند. هدف هر صفحه با framing برابر ۲۵۶ KiB و حداکثر ۱۲۸ کاربر است. یک رکورد نخست بزرگ‌تر می‌تواند به‌تنهایی تا سقف سخت پیام با کسر metadata و header جا بگیرد؛ رکورد غیرقابل‌جاگیری خطای مرتبط user-too-large می‌دهد و هیچ فیلدی truncate نمی‌شود. جدول بیش از ۱۶ MiB قابل همگام‌سازی است. sync-max-users پیش‌فرض 100000 و sync-max-bytes پیش‌فرض 268435456 دارند؛ هر دو عدد صحیح دقیق مثبت و قابل‌نمایش روی میزبان‌اند. بایت‌ها مجموع span دقیق object هر user، با whitespace داخلی و بدون metadata/کاما/bracket هستند و پیش از ساخت کاربران تازه بررسی می‌شوند. کل total از پیش reserve نمی‌شود. عبور از سقف sync_limit می‌دهد، staging را حذف و cache و بدهی را حفظ می‌کند. این سقف RAM دقیق نیست: جدول‌های active/baseline/ staging، indexها، JSON و صف‌ها حافظهٔ بیشتری می‌خواهند. تغییر تنظیم restart عادی می‌خواهد و crash recovery نمی‌آورد.

درخواست نخست epoch، sequence به شکل رشتهٔ ده‌دهی دقیق و digest با ۶۴ رقم hex کوچک برای آخرین ACK مطابق دارد؛ در enrollment، sequence برابر "0" و digest شامل ۶۴ صفر است. ادامه cursor و total ده‌دهی دارد. cursor شامل ۳۲ رقم instance، ۱۶ رقم config revision و ۱۶ رقم موقعیت بعدی است و مجوز محسوب نمی‌شود. هر درخواست token، مجوز pull و checkpoint زندهٔ producer را دوباره می‌سنجد. پاسخ فیلدهای instance، config-revision، stats-revision، total، position، count، cursor، complete، validated، epoch، sequence، digest، server-time-ms، accounting-time-ms و آرایهٔ credential-bearing users دارد؛ تمام اعداد ۶۴ بیتی رشتهٔ ده‌دهی دقیق‌اند.

صفحه‌ها پیوسته پیش می‌روند. جدول خالی یک صفحهٔ دادهٔ complete خالی دارد. پس از آخرین داده با complete:true و validated:false، کلاینت cursor/total نهایی و validate:true می‌فرستد؛ پاسخ تأیید validated:true، count صفر و users خالی دارد. سرور برای traversal یک snapshot تغییرناپذیر با سقف حافظه نگه می‌دارد؛ ویرایش config در میانهٔ دریافت آن را باطل نمی‌کند. اتمام بودجه یا انقضای idle ممکن است retry لازم کند. revision تنظیمات و آمار snapshot ثابت است و می‌تواند از revision زندهٔ envelope قدیمی‌تر باشد. هر صفحه نمونهٔ ساعت تازهٔ مخصوص همان پاسخ را دارد.

شکست traversal فقط staging را حذف می‌کند. با cache معتبر، مصرف تازه از baseline فعلی میان تلاش‌های دریافت گزارش می‌شود؛ هیچ batch تازه‌ای از traversal فعال عبور نمی‌کند. retry موعددار تنظیمات حتی زیر گزارش پیوسته نوبت می‌گیرد. بدهی ID حذف‌شده مانع نصب جدول نیست: بایت و مشاهدهٔ اولین مصرف در ledger محدود orphan می‌مانند و تا بازگشت همان ID ارسال نمی‌شوند. بدهی هرگز حذف نمی‌شود؛ بازگشت ID آن را به سهمیهٔ محلی برمی‌گرداند و فقط یک بار گزارش می‌کند. مبدأ محلی اولین مصرف و سن سپری‌شده پیش از projection ساعت تازه برمی‌گردند؛ غیبت حساب expiry را از ابتدا شروع نمی‌کند. handle به یک دورهٔ حضور پیوسته تعلق دارد: refresh معمولی معتبر می‌ماند، ولی حذف و ساخت دوبارهٔ همان ID یک کف generation تازه می‌سازد. handle قدیمی نمی‌تواند مصرف یا reservation حساب تازه را تغییر دهد؛ ticket قدیمی فقط هزینهٔ متعلق به خودش در client را آزاد می‌کند. سقف sync-max-bytes اوج حافظهٔ آماده‌سازی ledger شامل آرایهٔ قبلی، جایگزین، کپی موقت و فضای realloc را هم محدود می‌کند. اتمام این بودجه sync_limit می‌دهد و جدول قبلی و تمام بدهی را حفظ می‌کند. رد بودجهٔ capture کار معلق را نگه می‌دارد و حداکثر هر یک ثانیه دوباره تلاش می‌کند؛ حلقهٔ timer کوتاه ایجاد نمی‌شود.

تا validation نهایی generation و جدول عمومی عوض نمی‌شوند. ابتدا تمام کارهای fallible تمام، سپس delta زنده، مشاهدهٔ اولین مصرف، carry ضریب، reservation، صف و ticket با ID منتقل و active/baseline با هم publish می‌شوند. ساعت هر user پس از migration با نمونهٔ wall/accounting و receipt همان صفحه نصب می‌شود؛ تأخیر صفحه‌های بعدی سن را reset نمی‌کند. batch حل‌نشده پیش از صفحهٔ نخست replay می‌شود. تمام traversal و validation مانع batch تازه‌اند و first-use/push به‌صورت latch می‌مانند. تناقض checkpoint صفحه خطای continuity است و بدهی را پاک نمی‌کند.

مهلت absolute برای هر صفحه و validation جداست؛ کل traversal پیش‌رونده می‌تواند طولانی‌تر باشد. قطع، timeout، صفحهٔ malformed یا بدون پیشرفت، conflict و limit تنها staging را حذف می‌کنند. quiescence پس از settlement cleanup می‌کند. retry دریافت حداقل یک ثانیه فاصله دارد؛ retry عادی transport نخست interval اتصال مجدد را حفظ می‌کند، حتی صفر force-queued. شکست‌های بعدی از max(reconnect-interval-ms,1000) دوبرابر تا max(base,60000) می‌شوند و jitter افزوده تا یک‌چهارم دارند. کف refusal آمار ۳۰ ثانیه یا interval بزرگ‌تر نیز رعایت می‌شود. درخواست مستقیم، Resume، first-use و timer آن را دور نمی‌زنند. Session/Pong/صفحهٔ میانی streak را پاک نمی‌کند؛ نصب کامل پاک می‌کند. status شامل sync_retry_streak، sync_retry_remaining_ms، sync_retry_failure، orphan_debt_count، traversal_active، sync_staged_users، sync_staged_bytes و sync_total_users است. wakeup مستقل با intervalهای اختیاری صفر هم کار می‌کند.

خانوادهٔ WithResult خطای دقیق و WithProfile هویت کپی‌شدهٔ سازگار با handle می‌دهد؛ profile با authenticationclientUserProfileClear آزاد می‌شود. ObservePayload اولین مصرف را بدون charge ثبت و CommitDelivery بایت واقعاً تحویل‌شده را charge می‌کند. helperهای Bandwidth ticket/queue را مدیریت می‌کنند. UserAccountTraffic حسابداری فوری و UserAddTraffic قدیمی رفتار generation-sensitive دارد. handle/getter و pull/push صریح حفظ شده‌اند. سقف هر user در همین client برابر ۴ MiB بایت، ۸ MiB charge و ۱۰۲۴ buffer/ticket؛ سقف همین AuthenticationClient برابر ۶۴ MiB، ۱۲۸ MiB و ۱۶۳۸۴ است، با registryهای مستقل و بدون بودجهٔ مشترک میان clientها. secret متعلق به client، رشته‌های موقت Authenticate و رمز profile پیش از free امن پاک می‌شوند؛ کپی‌های config/transport/pool/dispatcher/OS ممکن است بمانند و پاک‌سازی سرتاسری تضمین نمی‌شود.