[{"data":1,"prerenderedAt":1229},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fv1\u002Fclient\u002Fsetup":121,"\u002Fdocs\u002Fv1\u002Fclient\u002Fsetup-surround":1224},[4],{"title":5,"path":6,"stem":7,"children":8,"page":44},"Docs","\u002Fdocs","docs",[9],{"title":10,"path":11,"stem":12,"children":13,"page":44},"V1","\u002Fdocs\u002Fv1","docs\u002Fv1",[14,18,22,45,58,80,93,111],{"title":15,"path":16,"stem":17},"Introduction","\u002Fdocs\u002Fv1\u002Fintroduction","docs\u002Fv1\u002F0.introduction",{"title":19,"path":20,"stem":21},"How It Works","\u002Fdocs\u002Fv1\u002Fhow-it-works","docs\u002Fv1\u002F1.how-it-works",{"title":23,"icon":24,"path":25,"stem":26,"children":27,"page":44},"Client","i-lucide-laptop","\u002Fdocs\u002Fv1\u002Fclient","docs\u002Fv1\u002F2.client",[28,32,36,40],{"title":29,"path":30,"stem":31},"Client Setup","\u002Fdocs\u002Fv1\u002Fclient\u002Fsetup","docs\u002Fv1\u002F2.client\u002F1.setup",{"title":33,"path":34,"stem":35},"Syncing","\u002Fdocs\u002Fv1\u002Fclient\u002Fsyncing","docs\u002Fv1\u002F2.client\u002F2.syncing",{"title":37,"path":38,"stem":39},"Events","\u002Fdocs\u002Fv1\u002Fclient\u002Fevents","docs\u002Fv1\u002F2.client\u002F3.events",{"title":41,"path":42,"stem":43},"Configuration","\u002Fdocs\u002Fv1\u002Fclient\u002Fconfiguration","docs\u002Fv1\u002F2.client\u002F4.configuration",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Server","i-lucide-server","\u002Fdocs\u002Fv1\u002Fserver","docs\u002Fv1\u002F3.server",[51,55],{"title":52,"path":53,"stem":54},"Server Setup","\u002Fdocs\u002Fv1\u002Fserver\u002Fsetup","docs\u002Fv1\u002F3.server\u002F1.setup",{"title":41,"path":56,"stem":57},"\u002Fdocs\u002Fv1\u002Fserver\u002Fconfiguration","docs\u002Fv1\u002F3.server\u002F2.configuration",{"title":59,"icon":60,"badge":61,"path":62,"stem":63,"children":64,"page":44},"NativePHP","i-lucide-smartphone","Commercial","\u002Fdocs\u002Fv1\u002Fnativephp","docs\u002Fv1\u002F4.nativephp",[65,69,73,76],{"title":66,"path":67,"stem":68},"Overview","\u002Fdocs\u002Fv1\u002Fnativephp\u002Foverview","docs\u002Fv1\u002F4.nativephp\u002F1.overview",{"title":70,"path":71,"stem":72},"Setup","\u002Fdocs\u002Fv1\u002Fnativephp\u002Fsetup","docs\u002Fv1\u002F4.nativephp\u002F2.setup",{"title":41,"path":74,"stem":75},"\u002Fdocs\u002Fv1\u002Fnativephp\u002Fconfiguration","docs\u002Fv1\u002F4.nativephp\u002F3.configuration",{"title":77,"path":78,"stem":79},"Lifecycle & Events","\u002Fdocs\u002Fv1\u002Fnativephp\u002Flifecycle","docs\u002Fv1\u002F4.nativephp\u002F4.lifecycle",{"title":81,"icon":82,"badge":61,"path":83,"stem":84,"children":85,"page":44},"Server Pro","i-lucide-bar-chart-2","\u002Fdocs\u002Fv1\u002Fserver-pro","docs\u002Fv1\u002F5.server-pro",[86,89],{"title":66,"path":87,"stem":88},"\u002Fdocs\u002Fv1\u002Fserver-pro\u002Foverview","docs\u002Fv1\u002F5.server-pro\u002F1.overview",{"title":90,"path":91,"stem":92},"Features","\u002Fdocs\u002Fv1\u002Fserver-pro\u002Ffeatures","docs\u002Fv1\u002F5.server-pro\u002F2.features",{"title":94,"icon":95,"path":96,"stem":97,"children":98,"page":44},"Advanced","i-lucide-settings-2","\u002Fdocs\u002Fv1\u002Fadvanced","docs\u002Fv1\u002F6.advanced",[99,103,107],{"title":100,"path":101,"stem":102},"Service Container","\u002Fdocs\u002Fv1\u002Fadvanced\u002Fservice-container","docs\u002Fv1\u002F6.advanced\u002F0.service-container",{"title":104,"path":105,"stem":106},"Conflict Resolution","\u002Fdocs\u002Fv1\u002Fadvanced\u002Fconflict-resolution","docs\u002Fv1\u002F6.advanced\u002F1.conflict-resolution",{"title":108,"path":109,"stem":110},"Payload Mapping","\u002Fdocs\u002Fv1\u002Fadvanced\u002Fpayload-mapping","docs\u002Fv1\u002F6.advanced\u002F2.payload-mapping",{"title":112,"icon":113,"path":114,"stem":115,"children":116,"page":44},"Examples","i-lucide-book-open","\u002Fdocs\u002Fv1\u002Fexamples","docs\u002Fv1\u002F7.examples",[117],{"title":118,"path":119,"stem":120},"Task Manager","\u002Fdocs\u002Fv1\u002Fexamples\u002Ftask-manager","docs\u002Fv1\u002F7.examples\u002F1.task-manager",{"id":122,"title":29,"body":123,"description":1217,"extension":1218,"links":1219,"meta":1220,"navigation":467,"path":30,"seo":1221,"stem":31,"__hash__":1223},"docs\u002Fdocs\u002Fv1\u002F2.client\u002F1.setup.md",{"type":124,"value":125,"toc":1205},"minimark",[126,139,142,147,194,197,237,239,243,246,393,408,419,421,425,432,598,608,610,614,621,670,703,759,765,767,771,777,835,838,840,844,855,858,860,864,871,900,903,947,950,1061,1063,1067,1074,1091,1094,1096,1100,1106,1160,1162,1166,1169,1194,1201],[127,128,129,133,134,138],"p",{},[130,131,132],"code",{},"tether\u002Fclient"," is the Laravel package you install in your ",[135,136,137],"strong",{},"client application"," - the local-first app that writes data while offline, records each mutation, and syncs those changes to your Laravel server later.",[140,141],"hr",{},[143,144,146],"h2",{"id":145},"installation","Installation",[148,149,154],"pre",{"className":150,"code":151,"language":152,"meta":153,"style":153},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","composer require tether\u002Fclient\nphp artisan tether:install\nphp artisan migrate\n","bash","",[130,155,156,172,184],{"__ignoreMap":153},[157,158,161,165,169],"span",{"class":159,"line":160},"line",1,[157,162,164],{"class":163},"sBMFI","composer",[157,166,168],{"class":167},"sfazB"," require",[157,170,171],{"class":167}," tether\u002Fclient\n",[157,173,175,178,181],{"class":159,"line":174},2,[157,176,177],{"class":163},"php",[157,179,180],{"class":167}," artisan",[157,182,183],{"class":167}," tether:install\n",[157,185,187,189,191],{"class":159,"line":186},3,[157,188,177],{"class":163},[157,190,180],{"class":167},[157,192,193],{"class":167}," migrate\n",[127,195,196],{},"The installer publishes the config file and creates two database tables:",[198,199,200,213],"table",{},[201,202,203],"thead",{},[204,205,206,210],"tr",{},[207,208,209],"th",{},"Table",[207,211,212],{},"Purpose",[214,215,216,227],"tbody",{},[204,217,218,224],{},[219,220,221],"td",{},[130,222,223],{},"tether_mutation_logs",[219,225,226],{},"Records every local create \u002F update \u002F delete mutation",[204,228,229,234],{},[219,230,231],{},[130,232,233],{},"tether_sync_state",[219,235,236],{},"Stores the last sync cursor, last sync time, client ID",[140,238],{},[143,240,242],{"id":241},"database-migration","Database migration",[127,244,245],{},"Every syncable model needs a sync identity column and standard Laravel timestamps:",[148,247,250],{"className":248,"code":249,"language":177,"meta":153,"style":153},"language-php shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","Schema::create('tasks', function (Blueprint $table) {\n    $table->id();\n    $table->tetherUlid();   \u002F\u002F tether_id char(26) unique nullable\n    $table->string('title');\n    $table->timestamps();\n    $table->softDeletes();\n});\n",[130,251,252,301,317,335,359,373,387],{"__ignoreMap":153},[157,253,254,257,261,265,268,271,274,276,279,283,286,289,292,295,298],{"class":159,"line":160},[157,255,256],{"class":163},"Schema",[157,258,260],{"class":259},"sMK4o","::",[157,262,264],{"class":263},"s2Zo4","create",[157,266,267],{"class":259},"(",[157,269,270],{"class":259},"'",[157,272,273],{"class":167},"tasks",[157,275,270],{"class":259},[157,277,278],{"class":259},",",[157,280,282],{"class":281},"spNyl"," function",[157,284,285],{"class":259}," (",[157,287,288],{"class":163},"Blueprint",[157,290,291],{"class":259}," $",[157,293,198],{"class":294},"sTEyZ",[157,296,297],{"class":259},")",[157,299,300],{"class":259}," {\n",[157,302,303,306,308,311,314],{"class":159,"line":174},[157,304,305],{"class":259},"    $",[157,307,198],{"class":294},[157,309,310],{"class":259},"->",[157,312,313],{"class":263},"id",[157,315,316],{"class":259},"();\n",[157,318,319,321,323,325,328,331],{"class":159,"line":186},[157,320,305],{"class":259},[157,322,198],{"class":294},[157,324,310],{"class":259},[157,326,327],{"class":263},"tetherUlid",[157,329,330],{"class":259},"();",[157,332,334],{"class":333},"sHwdD","   \u002F\u002F tether_id char(26) unique nullable\n",[157,336,338,340,342,344,347,349,351,354,356],{"class":159,"line":337},4,[157,339,305],{"class":259},[157,341,198],{"class":294},[157,343,310],{"class":259},[157,345,346],{"class":263},"string",[157,348,267],{"class":259},[157,350,270],{"class":259},[157,352,353],{"class":167},"title",[157,355,270],{"class":259},[157,357,358],{"class":259},");\n",[157,360,362,364,366,368,371],{"class":159,"line":361},5,[157,363,305],{"class":259},[157,365,198],{"class":294},[157,367,310],{"class":259},[157,369,370],{"class":263},"timestamps",[157,372,316],{"class":259},[157,374,376,378,380,382,385],{"class":159,"line":375},6,[157,377,305],{"class":259},[157,379,198],{"class":294},[157,381,310],{"class":259},[157,383,384],{"class":263},"softDeletes",[157,386,316],{"class":259},[157,388,390],{"class":159,"line":389},7,[157,391,392],{"class":259},"});\n",[127,394,395,396,399,400,403,404,407],{},"The ",[130,397,398],{},"tetherUlid()"," and ",[130,401,402],{},"dropTetherUlid()"," Blueprint macros are registered by ",[130,405,406],{},"tether\u002Fcore",", which is installed automatically as a dependency.",[127,409,410,411,414,415,418],{},"Tether relies on ",[130,412,413],{},"updated_at"," during pull reconciliation, so keep ",[130,416,417],{},"$table->timestamps()"," on every syncable model.",[140,420],{},[143,422,424],{"id":423},"making-a-model-syncable","Making a model syncable",[127,426,427,428,431],{},"Add the client ",[130,429,430],{},"Syncable"," trait to any Eloquent model you want to synchronise:",[148,433,435],{"className":248,"code":434,"language":177,"meta":153,"style":153},"use Tether\\Client\\Traits\\Syncable;\n\nclass Task extends Model\n{\n    use Syncable;\n\n    protected $fillable = ['title', 'status', 'due_date'];\n\n    protected array $syncable = ['title', 'status', 'due_date'];\n}\n",[130,436,437,463,469,483,488,498,502,546,551,592],{"__ignoreMap":153},[157,438,439,443,446,449,451,453,456,458,460],{"class":159,"line":160},[157,440,442],{"class":441},"sbssI","use",[157,444,445],{"class":294}," Tether",[157,447,448],{"class":259},"\\",[157,450,23],{"class":294},[157,452,448],{"class":259},[157,454,455],{"class":294},"Traits",[157,457,448],{"class":259},[157,459,430],{"class":294},[157,461,462],{"class":259},";\n",[157,464,465],{"class":159,"line":174},[157,466,468],{"emptyLinePlaceholder":467},true,"\n",[157,470,471,474,477,480],{"class":159,"line":186},[157,472,473],{"class":281},"class",[157,475,476],{"class":163}," Task",[157,478,479],{"class":281}," extends",[157,481,482],{"class":163}," Model\n",[157,484,485],{"class":159,"line":337},[157,486,487],{"class":259},"{\n",[157,489,490,493,496],{"class":159,"line":361},[157,491,492],{"class":441},"    use",[157,494,495],{"class":294}," Syncable",[157,497,462],{"class":259},[157,499,500],{"class":159,"line":375},[157,501,468],{"emptyLinePlaceholder":467},[157,503,504,507,509,512,515,518,520,522,524,526,529,532,534,536,538,541,543],{"class":159,"line":389},[157,505,506],{"class":281},"    protected",[157,508,291],{"class":259},[157,510,511],{"class":294},"fillable ",[157,513,514],{"class":259},"=",[157,516,517],{"class":259}," [",[157,519,270],{"class":259},[157,521,353],{"class":167},[157,523,270],{"class":259},[157,525,278],{"class":259},[157,527,528],{"class":259}," '",[157,530,531],{"class":167},"status",[157,533,270],{"class":259},[157,535,278],{"class":259},[157,537,528],{"class":259},[157,539,540],{"class":167},"due_date",[157,542,270],{"class":259},[157,544,545],{"class":259},"];\n",[157,547,549],{"class":159,"line":548},8,[157,550,468],{"emptyLinePlaceholder":467},[157,552,554,556,559,561,564,566,568,570,572,574,576,578,580,582,584,586,588,590],{"class":159,"line":553},9,[157,555,506],{"class":281},[157,557,558],{"class":441}," array",[157,560,291],{"class":259},[157,562,563],{"class":294},"syncable ",[157,565,514],{"class":259},[157,567,517],{"class":259},[157,569,270],{"class":259},[157,571,353],{"class":167},[157,573,270],{"class":259},[157,575,278],{"class":259},[157,577,528],{"class":259},[157,579,531],{"class":167},[157,581,270],{"class":259},[157,583,278],{"class":259},[157,585,528],{"class":259},[157,587,540],{"class":167},[157,589,270],{"class":259},[157,591,545],{"class":259},[157,593,595],{"class":159,"line":594},10,[157,596,597],{"class":259},"}\n",[127,599,600,601,604,605,607],{},"On create, the trait assigns a ULID to ",[130,602,603],{},"tether_id"," column we've just added in the migration. On create, update, and delete, trait also records a pending mutation in ",[130,606,223],{},". Your model's primary key is not affected and can be used in foreign key relationships as usual.",[140,609],{},[143,611,613],{"id":612},"controlling-which-fields-sync","Controlling which fields sync",[127,615,616,617,620],{},"The client payload is built from ",[130,618,619],{},"getSyncableFields()"," when sending create\u002Fupdate requests to the server.",[148,622,624],{"className":248,"code":623,"language":177,"meta":153,"style":153},"\u002F\u002F Explicit whitelist\nprotected array $syncable = ['title', 'status', 'due_date'];\n",[130,625,626,631],{"__ignoreMap":153},[157,627,628],{"class":159,"line":160},[157,629,630],{"class":333},"\u002F\u002F Explicit whitelist\n",[157,632,633,636,638,640,642,644,646,648,650,652,654,656,658,660,662,664,666,668],{"class":159,"line":174},[157,634,635],{"class":281},"protected",[157,637,558],{"class":441},[157,639,291],{"class":259},[157,641,563],{"class":294},[157,643,514],{"class":259},[157,645,517],{"class":259},[157,647,270],{"class":259},[157,649,353],{"class":167},[157,651,270],{"class":259},[157,653,278],{"class":259},[157,655,528],{"class":259},[157,657,531],{"class":167},[157,659,270],{"class":259},[157,661,278],{"class":259},[157,663,528],{"class":259},[157,665,540],{"class":167},[157,667,270],{"class":259},[157,669,545],{"class":259},[148,671,673],{"className":248,"code":672,"language":177,"meta":153,"style":153},"\u002F\u002F All fillable fields\nprotected array $syncable = ['*'];\n",[130,674,675,680],{"__ignoreMap":153},[157,676,677],{"class":159,"line":160},[157,678,679],{"class":333},"\u002F\u002F All fillable fields\n",[157,681,682,684,686,688,690,692,694,696,699,701],{"class":159,"line":174},[157,683,635],{"class":281},[157,685,558],{"class":441},[157,687,291],{"class":259},[157,689,563],{"class":294},[157,691,514],{"class":259},[157,693,517],{"class":259},[157,695,270],{"class":259},[157,697,698],{"class":167},"*",[157,700,270],{"class":259},[157,702,545],{"class":259},[148,704,706],{"className":248,"code":705,"language":177,"meta":153,"style":153},"\u002F\u002F All fillable fields except these\nprotected array $syncable = ['*'];\nprotected array $syncableExcept = ['internal_notes'];\n",[130,707,708,713,735],{"__ignoreMap":153},[157,709,710],{"class":159,"line":160},[157,711,712],{"class":333},"\u002F\u002F All fillable fields except these\n",[157,714,715,717,719,721,723,725,727,729,731,733],{"class":159,"line":174},[157,716,635],{"class":281},[157,718,558],{"class":441},[157,720,291],{"class":259},[157,722,563],{"class":294},[157,724,514],{"class":259},[157,726,517],{"class":259},[157,728,270],{"class":259},[157,730,698],{"class":167},[157,732,270],{"class":259},[157,734,545],{"class":259},[157,736,737,739,741,743,746,748,750,752,755,757],{"class":159,"line":186},[157,738,635],{"class":281},[157,740,558],{"class":441},[157,742,291],{"class":259},[157,744,745],{"class":294},"syncableExcept ",[157,747,514],{"class":259},[157,749,517],{"class":259},[157,751,270],{"class":259},[157,753,754],{"class":167},"internal_notes",[157,756,270],{"class":259},[157,758,545],{"class":259},[127,760,761,762,764],{},"Deletes send an empty payload; the ",[130,763,603],{}," is enough to identify the record.",[140,766],{},[143,768,770],{"id":769},"custom-sync-key-column","Custom sync key column",[127,772,773,774,776],{},"By default, Tether uses ",[130,775,603],{},". To use a different column per model:",[148,778,780],{"className":248,"code":779,"language":177,"meta":153,"style":153},"class Task extends Model\n{\n    use Syncable;\n\n    protected string $tetherKeyName = 'sync_id';\n}\n",[130,781,782,792,796,804,808,831],{"__ignoreMap":153},[157,783,784,786,788,790],{"class":159,"line":160},[157,785,473],{"class":281},[157,787,476],{"class":163},[157,789,479],{"class":281},[157,791,482],{"class":163},[157,793,794],{"class":159,"line":174},[157,795,487],{"class":259},[157,797,798,800,802],{"class":159,"line":186},[157,799,492],{"class":441},[157,801,495],{"class":294},[157,803,462],{"class":259},[157,805,806],{"class":159,"line":337},[157,807,468],{"emptyLinePlaceholder":467},[157,809,810,812,815,817,820,822,824,827,829],{"class":159,"line":361},[157,811,506],{"class":281},[157,813,814],{"class":441}," string",[157,816,291],{"class":259},[157,818,819],{"class":294},"tetherKeyName ",[157,821,514],{"class":259},[157,823,528],{"class":259},[157,825,826],{"class":167},"sync_id",[157,828,270],{"class":259},[157,830,462],{"class":259},[157,832,833],{"class":159,"line":375},[157,834,597],{"class":259},[127,836,837],{},"Update the migration and make sure the server uses the same sync key for that model.",[140,839],{},[143,841,843],{"id":842},"soft-deletes","Soft deletes",[127,845,846,847,850,851,854],{},"If your model uses ",[130,848,849],{},"SoftDeletes",", Tether captures delete events as ",[130,852,853],{},"delete"," mutations. On pull, server soft-deleted rows arrive as delete snapshots so clients can mirror the deletion.",[127,856,857],{},"Hard-deleted server rows cannot be discovered by state-based pull, so use soft deletes for records that need deletion propagation on client side.",[140,859],{},[143,861,863],{"id":862},"client-identity","Client identity",[127,865,866,867,870],{},"Every sync request includes a ",[130,868,869],{},"client_id",". Tether uses it to identify the client making the request. Tether resolves it in this order:",[872,873,874,881,887,895],"ol",{},[875,876,877,878],"li",{},"Callable registered with ",[130,879,880],{},"TetherClientServiceProvider::resolveClientIdUsing()",[875,882,883,884],{},"Invokable class configured in ",[130,885,886],{},"client_id_resolver",[875,888,889,892,893],{},[130,890,891],{},"TETHER_CLIENT_ID"," \u002F ",[130,894,869],{},[875,896,897,898],{},"Auto-generated ULID persisted in ",[130,899,233],{},[127,901,902],{},"For a config-cache-safe dynamic resolver:",[148,904,906],{"className":248,"code":905,"language":177,"meta":153,"style":153},"\u002F\u002F config\u002Ftether-client.php\n'client_id_resolver' => \\App\\Tether\\DeviceIdResolver::class,\n",[130,907,908,913],{"__ignoreMap":153},[157,909,910],{"class":159,"line":160},[157,911,912],{"class":333},"\u002F\u002F config\u002Ftether-client.php\n",[157,914,915,917,919,921,924,927,930,932,935,937,940,942,944],{"class":159,"line":174},[157,916,270],{"class":259},[157,918,886],{"class":167},[157,920,270],{"class":259},[157,922,923],{"class":259}," =>",[157,925,926],{"class":259}," \\",[157,928,929],{"class":294},"App",[157,931,448],{"class":259},[157,933,934],{"class":294},"Tether",[157,936,448],{"class":259},[157,938,939],{"class":163},"DeviceIdResolver",[157,941,260],{"class":259},[157,943,473],{"class":441},[157,945,946],{"class":259},",\n",[127,948,949],{},"For runtime resolution:",[148,951,953],{"className":248,"code":952,"language":177,"meta":153,"style":153},"use Tether\\Client\\TetherClientServiceProvider;\n\npublic function boot(): void\n{\n    TetherClientServiceProvider::resolveClientIdUsing(\n        fn () => auth()->user()?->device_id ?? config('tether-client.client_id')\n    );\n}\n",[130,954,955,972,976,992,996,1009,1052,1057],{"__ignoreMap":153},[157,956,957,959,961,963,965,967,970],{"class":159,"line":160},[157,958,442],{"class":441},[157,960,445],{"class":294},[157,962,448],{"class":259},[157,964,23],{"class":294},[157,966,448],{"class":259},[157,968,969],{"class":294},"TetherClientServiceProvider",[157,971,462],{"class":259},[157,973,974],{"class":159,"line":174},[157,975,468],{"emptyLinePlaceholder":467},[157,977,978,981,983,986,989],{"class":159,"line":186},[157,979,980],{"class":281},"public",[157,982,282],{"class":281},[157,984,985],{"class":263}," boot",[157,987,988],{"class":259},"():",[157,990,991],{"class":441}," void\n",[157,993,994],{"class":159,"line":337},[157,995,487],{"class":259},[157,997,998,1001,1003,1006],{"class":159,"line":361},[157,999,1000],{"class":163},"    TetherClientServiceProvider",[157,1002,260],{"class":259},[157,1004,1005],{"class":263},"resolveClientIdUsing",[157,1007,1008],{"class":259},"(\n",[157,1010,1011,1014,1017,1019,1022,1025,1028,1031,1034,1037,1040,1042,1044,1047,1049],{"class":159,"line":375},[157,1012,1013],{"class":281},"        fn",[157,1015,1016],{"class":259}," ()",[157,1018,923],{"class":259},[157,1020,1021],{"class":263}," auth",[157,1023,1024],{"class":259},"()->",[157,1026,1027],{"class":263},"user",[157,1029,1030],{"class":259},"()?->",[157,1032,1033],{"class":294},"device_id ",[157,1035,1036],{"class":259},"??",[157,1038,1039],{"class":263}," config",[157,1041,267],{"class":259},[157,1043,270],{"class":259},[157,1045,1046],{"class":167},"tether-client.client_id",[157,1048,270],{"class":259},[157,1050,1051],{"class":259},")\n",[157,1053,1054],{"class":159,"line":389},[157,1055,1056],{"class":259},"    );\n",[157,1058,1059],{"class":159,"line":548},[157,1060,597],{"class":259},[140,1062],{},[143,1064,1066],{"id":1065},"server-url-configuration","Server URL configuration",[127,1068,1069,1070,1073],{},"Set the push and pull endpoint URLs in ",[130,1071,1072],{},".env",":",[148,1075,1079],{"className":1076,"code":1077,"language":1078,"meta":153,"style":153},"language-env shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","TETHER_SERVER_PUSH_URL=https:\u002F\u002Fyour-server.example.com\u002Ftether\u002Fpush\nTETHER_SERVER_PULL_URL=https:\u002F\u002Fyour-server.example.com\u002Ftether\u002Fpull\n","env",[130,1080,1081,1086],{"__ignoreMap":153},[157,1082,1083],{"class":159,"line":160},[157,1084,1085],{},"TETHER_SERVER_PUSH_URL=https:\u002F\u002Fyour-server.example.com\u002Ftether\u002Fpush\n",[157,1087,1088],{"class":159,"line":174},[157,1089,1090],{},"TETHER_SERVER_PULL_URL=https:\u002F\u002Fyour-server.example.com\u002Ftether\u002Fpull\n",[127,1092,1093],{},"Push and pull can point to different hosts if needed.",[140,1095],{},[143,1097,1099],{"id":1098},"mutation-log-statuses","Mutation log statuses",[127,1101,1102,1103,1105],{},"Every entry in ",[130,1104,223],{}," uses one of these statuses:",[198,1107,1108,1118],{},[201,1109,1110],{},[204,1111,1112,1115],{},[207,1113,1114],{},"Status",[207,1116,1117],{},"Meaning",[214,1119,1120,1130,1140,1150],{},[204,1121,1122,1127],{},[219,1123,1124],{},[130,1125,1126],{},"pending",[219,1128,1129],{},"Waiting to be pushed to the server",[204,1131,1132,1137],{},[219,1133,1134],{},[130,1135,1136],{},"synced",[219,1138,1139],{},"Successfully applied on the server",[204,1141,1142,1147],{},[219,1143,1144],{},[130,1145,1146],{},"failed",[219,1148,1149],{},"Rejected by the server or exhausted retries",[204,1151,1152,1157],{},[219,1153,1154],{},[130,1155,1156],{},"conflict",[219,1158,1159],{},"Server detected a conflict; server state was applied locally",[140,1161],{},[143,1163,1165],{"id":1164},"pull-page-size","Pull page size",[127,1167,1168],{},"By default, a pull fetches all changed snapshots in one request. For large datasets, set a page size so the client issues multiple requests automatically:",[148,1170,1172],{"className":248,"code":1171,"language":177,"meta":153,"style":153},"\u002F\u002F config\u002Ftether-client.php\n'pull_page_size' => 500,\n",[130,1173,1174,1178],{"__ignoreMap":153},[157,1175,1176],{"class":159,"line":160},[157,1177,912],{"class":333},[157,1179,1180,1182,1185,1187,1189,1192],{"class":159,"line":174},[157,1181,270],{"class":259},[157,1183,1184],{"class":167},"pull_page_size",[157,1186,270],{"class":259},[157,1188,923],{"class":259},[157,1190,1191],{"class":441}," 500",[157,1193,946],{"class":259},[127,1195,1196,1197,1200],{},"When ",[130,1198,1199],{},"null"," (the default), the server returns all changed records in scope in a single response.",[1202,1203,1204],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":153,"searchDepth":160,"depth":174,"links":1206},[1207,1208,1209,1210,1211,1212,1213,1214,1215,1216],{"id":145,"depth":174,"text":146},{"id":241,"depth":174,"text":242},{"id":423,"depth":174,"text":424},{"id":612,"depth":174,"text":613},{"id":769,"depth":174,"text":770},{"id":842,"depth":174,"text":843},{"id":862,"depth":174,"text":863},{"id":1065,"depth":174,"text":1066},{"id":1098,"depth":174,"text":1099},{"id":1164,"depth":174,"text":1165},"Install tether\u002Fclient, configure a local-first Laravel client app, add Syncable models, and record offline mutations for later sync.","md",null,{},{"title":1222,"description":1217},"Laravel Offline Sync Client Setup - Laravel Tether","IBpLU0ieNFXfHMJCudbH-6wZmOMU0z0Wog1yLq9BOMA",[1225,1227],{"title":19,"path":20,"stem":21,"description":1226,"children":-1},"Understand Tether's offline sync architecture for Laravel: mutation-log push, state-snapshot pull, ULID identity, idempotency, and conflict handling.",{"title":33,"path":34,"stem":35,"description":1228,"children":-1},"Run Laravel Tether sync manually, from queues, or on a schedule, and understand push\u002Fpull results, conflicts, retries, and pull pagination.",1780481013060]