[{"data":1,"prerenderedAt":749},["ShallowReactive",2],{"navigation_docs_en":3,"-en-self-hosting-security-and-networking":123,"-en-self-hosting-security-and-networking-surround":744},[4,36,107],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":35},"Getting started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,25,30],{"title":11,"path":12,"stem":13,"icon":14},"What is Chronicler","\u002Fen\u002Fgetting-started\u002Fwhat-is-chronicler","en\u002F1.getting-started\u002F1.what-is-chronicler","i-lucide-help-circle",{"title":16,"path":17,"stem":18,"icon":19},"Install the app","\u002Fen\u002Fgetting-started\u002Finstall-the-app","en\u002F1.getting-started\u002F2.install-the-app","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Cloud quick start","\u002Fen\u002Fgetting-started\u002Fcloud-quickstart","en\u002F1.getting-started\u002F3.cloud-quickstart","i-lucide-cloud",{"title":26,"path":27,"stem":28,"icon":29},"Your first chat","\u002Fen\u002Fgetting-started\u002Fyour-first-chat","en\u002F1.getting-started\u002F4.your-first-chat","i-lucide-messages-square",{"title":31,"path":32,"stem":33,"icon":34},"Managing users","\u002Fen\u002Fgetting-started\u002Fmanaging-users","en\u002F1.getting-started\u002F5.managing-users","i-lucide-users",false,{"title":37,"icon":38,"path":39,"stem":40,"children":41,"page":35},"Self-hosting","i-lucide-server","\u002Fen\u002Fself-hosting","en\u002F2.self-hosting",[42,47,52,57,62,67,72,77,82,87,92,97,102],{"title":43,"path":44,"stem":45,"icon":46},"Overview","\u002Fen\u002Fself-hosting\u002Foverview","en\u002F2.self-hosting\u002F1.overview","i-lucide-layout-dashboard",{"title":48,"path":49,"stem":50,"icon":51},"Backups and restore","\u002Fen\u002Fself-hosting\u002Fbackups","en\u002F2.self-hosting\u002F10.backups","i-lucide-database-backup",{"title":53,"path":54,"stem":55,"icon":56},"Updating","\u002Fen\u002Fself-hosting\u002Fupdating","en\u002F2.self-hosting\u002F11.updating","i-lucide-refresh-cw",{"title":58,"path":59,"stem":60,"icon":61},"Security and networking","\u002Fen\u002Fself-hosting\u002Fsecurity-and-networking","en\u002F2.self-hosting\u002F12.security-and-networking","i-lucide-shield",{"title":63,"path":64,"stem":65,"icon":66},"Troubleshooting","\u002Fen\u002Fself-hosting\u002Ftroubleshooting","en\u002F2.self-hosting\u002F13.troubleshooting","i-lucide-life-buoy",{"title":68,"path":69,"stem":70,"icon":71},"Requirements","\u002Fen\u002Fself-hosting\u002Frequirements","en\u002F2.self-hosting\u002F2.requirements","i-lucide-cpu",{"title":73,"path":74,"stem":75,"icon":76},"Quick start (guided)","\u002Fen\u002Fself-hosting\u002Fquick-start","en\u002F2.self-hosting\u002F3.quick-start","i-lucide-wand-2",{"title":78,"path":79,"stem":80,"icon":81},"Manual install","\u002Fen\u002Fself-hosting\u002Fmanual-install","en\u002F2.self-hosting\u002F4.manual-install","i-lucide-terminal",{"title":83,"path":84,"stem":85,"icon":86},"GPU acceleration","\u002Fen\u002Fself-hosting\u002Fgpu-acceleration","en\u002F2.self-hosting\u002F5.gpu-acceleration","i-lucide-zap",{"title":88,"path":89,"stem":90,"icon":91},"Performance and memory","\u002Fen\u002Fself-hosting\u002Fperformance-and-memory","en\u002F2.self-hosting\u002F6.performance-and-memory","i-lucide-gauge",{"title":93,"path":94,"stem":95,"icon":96},"Configuration reference","\u002Fen\u002Fself-hosting\u002Fconfiguration-reference","en\u002F2.self-hosting\u002F7.configuration-reference","i-lucide-sliders-horizontal",{"title":98,"path":99,"stem":100,"icon":101},"Models","\u002Fen\u002Fself-hosting\u002Fmodels","en\u002F2.self-hosting\u002F8.models","i-lucide-brain",{"title":103,"path":104,"stem":105,"icon":106},"Licensing and activation","\u002Fen\u002Fself-hosting\u002Flicensing","en\u002F2.self-hosting\u002F9.licensing","i-lucide-key-round",{"title":108,"icon":109,"path":110,"stem":111,"children":112,"page":35},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F3.reference",[113,118],{"title":114,"path":115,"stem":116,"icon":117},"How it works","\u002Fen\u002Freference\u002Fhow-it-works","en\u002F3.reference\u002F1.how-it-works","i-lucide-workflow",{"title":119,"path":120,"stem":121,"icon":122},"Data and privacy","\u002Fen\u002Freference\u002Fdata-and-privacy","en\u002F3.reference\u002F2.data-and-privacy","i-lucide-lock",{"id":124,"title":58,"body":125,"description":735,"extension":736,"links":737,"meta":738,"navigation":739,"path":59,"seo":740,"stem":60,"__hash__":743},"docs_en\u002Fen\u002F2.self-hosting\u002F12.security-and-networking.md",{"type":126,"value":127,"toc":725},"minimark",[128,133,199,209,248,252,264,271,275,281,326,337,341,345,348,518,529,532,554,560,564,568,625,631,635,655,659,665,669,721],[129,130,132],"h2",{"id":131},"whats-exposed","What's exposed",[134,135,136,152],"table",{},[137,138,139],"thead",{},[140,141,142,146,149],"tr",{},[143,144,145],"th",{},"Port",[143,147,148],{},"Service",[143,150,151],{},"Who should reach it",[153,154,155,170,183],"tbody",{},[140,156,157,164,167],{},[158,159,160],"td",{},[161,162,163],"code",{},"8410",[158,165,166],{},"Backend API",[158,168,169],{},"Every machine running Chronicler",[140,171,172,177,180],{},[158,173,174],{},[161,175,176],{},"11434",[158,178,179],{},"Model runtime",[158,181,182],{},"Nothing outside the server",[140,184,185,190,193],{},[158,186,187],{},[161,188,189],{},"5432",[158,191,192],{},"Database",[158,194,195,196],{},"Nothing — bound to ",[161,197,198],{},"127.0.0.1",[200,201,202,203,205,206,208],"p",{},"Only ",[161,204,163],{}," needs to be reachable. If the compose file you're using publishes\n",[161,207,176],{}," more widely than you'd like, restrict it the same way Postgres is:",[210,211,217],"pre",{"className":212,"code":213,"filename":214,"language":215,"meta":216,"style":216},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","    ports:\n      - \"127.0.0.1:11434:11434\"\n","docker-compose.yml","yaml","",[161,218,219,232],{"__ignoreMap":216},[220,221,224,228],"span",{"class":222,"line":223},"line",1,[220,225,227],{"class":226},"swJcz","    ports",[220,229,231],{"class":230},"sMK4o",":\n",[220,233,235,238,241,245],{"class":222,"line":234},2,[220,236,237],{"class":230},"      -",[220,239,240],{"class":230}," \"",[220,242,244],{"class":243},"sfazB","127.0.0.1:11434:11434",[220,246,247],{"class":230},"\"\n",[129,249,251],{"id":250},"claim-the-server-immediately","Claim the server immediately",[200,253,254,255,259,260,263],{},"A server with no users offers ",[256,257,258],"strong",{},"Register your admin account"," to whoever reaches\nit first. Between ",[161,261,262],{},"docker compose up -d"," and creating that account, anyone who\ncan reach port 8410 can take it.",[200,265,266,267,270],{},"So: start the stack and register the admin ",[256,268,269],{},"in the same sitting",", from a\nmachine you trust, before opening the port to a wider network.",[129,272,274],{"id":273},"on-a-local-network","On a local network",[200,276,277,278,280],{},"For a server and its users on the same trusted LAN, plain HTTP on ",[161,279,163],{}," is\nusually accepted — the traffic never leaves the building. Restrict it to the\nsubnet at the firewall:",[210,282,287],{"className":283,"code":284,"filename":285,"language":286,"meta":216,"style":216},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Linux, ufw — allow only the office subnet\nsudo ufw allow from 192.168.1.0\u002F24 to any port 8410\n","Terminal","bash",[161,288,289,295],{"__ignoreMap":216},[220,290,291],{"class":222,"line":223},[220,292,294],{"class":293},"sHwdD","# Linux, ufw — allow only the office subnet\n",[220,296,297,301,304,307,310,313,316,319,322],{"class":222,"line":234},[220,298,300],{"class":299},"sBMFI","sudo",[220,302,303],{"class":243}," ufw",[220,305,306],{"class":243}," allow",[220,308,309],{"class":243}," from",[220,311,312],{"class":243}," 192.168.1.0\u002F24",[220,314,315],{"class":243}," to",[220,317,318],{"class":243}," any",[220,320,321],{"class":243}," port",[220,323,325],{"class":324},"sbssI"," 8410\n",[200,327,328,329,332,333,336],{},"Colleagues then point ",[256,330,331],{},"Settings → Connection → Local"," at\n",[161,334,335],{},"http:\u002F\u002F192.168.1.50:8410",".",[129,338,340],{"id":339},"over-the-internet-use-https","Over the internet: use HTTPS",[342,343,344],"caution",{},"Do not publish port 8410 to the internet directly. Sign-ins and document content\nwould cross the network in clear text.",[200,346,347],{},"Put a reverse proxy in front. Chronicler ships no proxy and no certificates —\nthat stays your choice.",[349,350,351,463],"code-group",{},[210,352,356],{"className":353,"code":354,"filename":355,"language":355,"meta":216,"style":216},"language-nginx shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","server {\n    listen 443 ssl;\n    server_name chronicler.example.com;\n\n    ssl_certificate     \u002Fetc\u002Fletsencrypt\u002Flive\u002Fchronicler.example.com\u002Ffullchain.pem;\n    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fchronicler.example.com\u002Fprivkey.pem;\n\n    client_max_body_size 512M;   # uploads\n\n    location \u002F {\n        proxy_pass http:\u002F\u002F127.0.0.1:8410;\n        proxy_set_header Host              $host;\n        proxy_set_header X-Real-IP         $remote_addr;\n        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_read_timeout 600s;     # answers can take minutes on CPU\n    }\n}\n","nginx",[161,357,358,363,368,374,381,387,393,398,404,409,415,421,427,433,439,445,451,457],{"__ignoreMap":216},[220,359,360],{"class":222,"line":223},[220,361,362],{},"server {\n",[220,364,365],{"class":222,"line":234},[220,366,367],{},"    listen 443 ssl;\n",[220,369,371],{"class":222,"line":370},3,[220,372,373],{},"    server_name chronicler.example.com;\n",[220,375,377],{"class":222,"line":376},4,[220,378,380],{"emptyLinePlaceholder":379},true,"\n",[220,382,384],{"class":222,"line":383},5,[220,385,386],{},"    ssl_certificate     \u002Fetc\u002Fletsencrypt\u002Flive\u002Fchronicler.example.com\u002Ffullchain.pem;\n",[220,388,390],{"class":222,"line":389},6,[220,391,392],{},"    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fchronicler.example.com\u002Fprivkey.pem;\n",[220,394,396],{"class":222,"line":395},7,[220,397,380],{"emptyLinePlaceholder":379},[220,399,401],{"class":222,"line":400},8,[220,402,403],{},"    client_max_body_size 512M;   # uploads\n",[220,405,407],{"class":222,"line":406},9,[220,408,380],{"emptyLinePlaceholder":379},[220,410,412],{"class":222,"line":411},10,[220,413,414],{},"    location \u002F {\n",[220,416,418],{"class":222,"line":417},11,[220,419,420],{},"        proxy_pass http:\u002F\u002F127.0.0.1:8410;\n",[220,422,424],{"class":222,"line":423},12,[220,425,426],{},"        proxy_set_header Host              $host;\n",[220,428,430],{"class":222,"line":429},13,[220,431,432],{},"        proxy_set_header X-Real-IP         $remote_addr;\n",[220,434,436],{"class":222,"line":435},14,[220,437,438],{},"        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;\n",[220,440,442],{"class":222,"line":441},15,[220,443,444],{},"        proxy_set_header X-Forwarded-Proto $scheme;\n",[220,446,448],{"class":222,"line":447},16,[220,449,450],{},"        proxy_read_timeout 600s;     # answers can take minutes on CPU\n",[220,452,454],{"class":222,"line":453},17,[220,455,456],{},"    }\n",[220,458,460],{"class":222,"line":459},18,[220,461,462],{},"}\n",[210,464,469],{"className":465,"code":466,"filename":467,"language":468,"meta":216,"style":216},"language-caddy shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","chronicler.example.com {\n    reverse_proxy 127.0.0.1:8410 {\n        transport http {\n            read_timeout 600s\n        }\n    }\n    request_body {\n        max_size 512MB\n    }\n}\n","Caddyfile","caddy",[161,470,471,476,481,486,491,496,500,505,510,514],{"__ignoreMap":216},[220,472,473],{"class":222,"line":223},[220,474,475],{},"chronicler.example.com {\n",[220,477,478],{"class":222,"line":234},[220,479,480],{},"    reverse_proxy 127.0.0.1:8410 {\n",[220,482,483],{"class":222,"line":370},[220,484,485],{},"        transport http {\n",[220,487,488],{"class":222,"line":376},[220,489,490],{},"            read_timeout 600s\n",[220,492,493],{"class":222,"line":383},[220,494,495],{},"        }\n",[220,497,498],{"class":222,"line":389},[220,499,456],{},[220,501,502],{"class":222,"line":395},[220,503,504],{},"    request_body {\n",[220,506,507],{"class":222,"line":400},[220,508,509],{},"        max_size 512MB\n",[220,511,512],{"class":222,"line":406},[220,513,456],{},[220,515,516],{"class":222,"line":411},[220,517,462],{},[200,519,520,521,524,525,528],{},"Two settings matter and are easy to miss: a ",[256,522,523],{},"generous body size"," (documents are\nlarge) and a ",[256,526,527],{},"long read timeout"," (a CPU-generated answer can take minutes; the\nusual 60-second default will cut it off).",[200,530,531],{},"Then bind the backend to loopback only, so the proxy is the only way in:",[210,533,535],{"className":212,"code":534,"filename":214,"language":215,"meta":216,"style":216},"    ports:\n      - \"127.0.0.1:8410:8410\"\n",[161,536,537,543],{"__ignoreMap":216},[220,538,539,541],{"class":222,"line":223},[220,540,227],{"class":226},[220,542,231],{"class":230},[220,544,545,547,549,552],{"class":222,"line":234},[220,546,237],{"class":230},[220,548,240],{"class":230},[220,550,551],{"class":243},"127.0.0.1:8410:8410",[220,553,247],{"class":230},[200,555,556,557,336],{},"Users enter ",[161,558,559],{},"https:\u002F\u002Fchronicler.example.com",[561,562,563],"tip",{},"A VPN is often the better answer. If everyone already connects to the office\nnetwork remotely, keep Chronicler on the LAN and publish nothing.",[129,565,567],{"id":566},"secrets-to-protect","Secrets to protect",[134,569,570,583],{},[137,571,572],{},[140,573,574,577,580],{},[143,575,576],{},"What",[143,578,579],{},"Where",[143,581,582],{},"Why it matters",[153,584,585,600,614],{},[140,586,587,592,597],{},[158,588,589],{},[161,590,591],{},"POSTGRES_PASSWORD",[158,593,594],{},[161,595,596],{},".env",[158,598,599],{},"The only key to your database volume. Lose it and the data is unreadable; leak it and anyone who can reach the database has everything.",[140,601,602,605,611],{},[158,603,604],{},"Backup archives",[158,606,607,610],{},[161,608,609],{},"backend_uploads",", plus your offsite folder",[158,612,613],{},"A full copy of the database. Protect them like the database itself.",[140,615,616,619,622],{},[158,617,618],{},"Licence key",[158,620,621],{},"Admin console",[158,623,624],{},"Bound to one install, but still yours.",[200,626,627,628,630],{},"The server's signing key is generated on first start and kept in the\n",[161,629,609],{}," volume. You never handle it; just don't delete that volume.",[129,632,634],{"id":633},"accounts","Accounts",[636,637,638,646,649,652],"ul",{},[639,640,641,642,645],"li",{},"Nobody can sign themselves up. Administrators create accounts\n(",[643,644,31],"a",{"href":32},").",[639,647,648],{},"New accounts get a one-time password, shown once, and must set their own on\nfirst sign-in.",[639,650,651],{},"Suspend accounts when people leave — suspension keeps their documents, deletion\ndoesn't.",[639,653,654],{},"Keep the number of administrators small. Administrators can restore backups,\nwhich means reading everything.",[129,656,658],{"id":657},"keeping-it-patched","Keeping it patched",[200,660,661,662,664],{},"Update monthly (",[643,663,53],{"href":54},"), keep Docker itself\nupdated, and patch the host OS. The stack is small — four containers, no plugins,\nno extensions — which is the point.",[129,666,668],{"id":667},"a-short-checklist","A short checklist",[636,670,673,682,688,694,703,709,715],{"className":671},[672],"contains-task-list",[639,674,677,681],{"className":675},[676],"task-list-item",[678,679],"input",{"disabled":379,"type":680},"checkbox"," Admin account registered immediately after first start",[639,683,685,687],{"className":684},[676],[678,686],{"disabled":379,"type":680}," Port 8410 restricted to the network that needs it",[639,689,691,693],{"className":690},[676],[678,692],{"disabled":379,"type":680}," HTTPS in front if it's reachable from outside the LAN",[639,695,697,699,700,702],{"className":696},[676],[678,698],{"disabled":379,"type":680}," ",[161,701,596],{}," backed up somewhere safe, separate from the server",[639,704,706,708],{"className":705},[676],[678,707],{"disabled":379,"type":680}," Scheduled backups on, with an offsite copy",[639,710,712,714],{"className":711},[676],[678,713],{"disabled":379,"type":680}," One restore rehearsed at least once",[639,716,718,720],{"className":717},[676],[678,719],{"disabled":379,"type":680}," Administrator accounts kept to a minimum",[722,723,724],"style",{},"html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}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 .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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":216,"searchDepth":234,"depth":234,"links":726},[727,728,729,730,731,732,733,734],{"id":131,"depth":234,"text":132},{"id":250,"depth":234,"text":251},{"id":273,"depth":234,"text":274},{"id":339,"depth":234,"text":340},{"id":566,"depth":234,"text":567},{"id":633,"depth":234,"text":634},{"id":657,"depth":234,"text":658},{"id":667,"depth":234,"text":668},"Let colleagues in without letting everyone in.","md",null,{},{"icon":61},{"title":741,"description":742},"Chronicler self-hosting security and networking","Expose the Chronicler backend safely, add HTTPS, and protect the database and backups.","FjYWmlF8sDKzUk-4KKv1mxmf7a-XwErepHS6k8vIyiM",[745,747],{"title":53,"path":54,"stem":55,"description":746,"icon":56,"children":-1},"The app and the server update separately. Here's how each one works.",{"title":63,"path":64,"stem":65,"description":748,"icon":66,"children":-1},"The failures people actually hit, and what fixes each one.",1785092109430]