[{"data":1,"prerenderedAt":762},["ShallowReactive",2],{"navigation":3,"\u002Freference\u002Fenv":80,"\u002Freference\u002Fenv-surround":757},[4,9,14,50,75],{"title":5,"path":6,"stem":7,"icon":8},"Getting started","\u002Fgetting-started","1.getting-started","i-lucide-rocket",{"title":10,"path":11,"stem":12,"icon":13},"Installation","\u002Finstallation","2.installation","i-lucide-hard-drive-download",{"title":15,"path":16,"stem":17,"children":18,"page":49},"Guide","\u002Fguide","3.guide",[19,24,29,34,39,44],{"title":20,"path":21,"stem":22,"icon":23},"Timer & entries","\u002Fguide\u002Ftimer-and-entries","3.guide\u002F1.timer-and-entries","i-lucide-timer",{"title":25,"path":26,"stem":27,"icon":28},"Projects, clients & tasks","\u002Fguide\u002Fprojects-clients-tasks","3.guide\u002F2.projects-clients-tasks","i-lucide-git-branch",{"title":30,"path":31,"stem":32,"icon":33},"Reports & calendar","\u002Fguide\u002Freports-calendar","3.guide\u002F3.reports-calendar","i-lucide-chart-column",{"title":35,"path":36,"stem":37,"icon":38},"Theme editor","\u002Fguide\u002Ftheme-editor","3.guide\u002F4.theme-editor","i-lucide-palette",{"title":40,"path":41,"stem":42,"icon":43},"Importing data","\u002Fguide\u002Fimport","3.guide\u002F5.import","i-lucide-hard-drive-upload",{"title":45,"path":46,"stem":47,"icon":48},"Team & organizations","\u002Fguide\u002Fteam","3.guide\u002F6.team","i-lucide-users",false,{"title":51,"path":52,"stem":53,"children":54,"page":49},"Reference","\u002Freference","4.reference",[55,60,65,70],{"title":56,"path":57,"stem":58,"icon":59},"Data model","\u002Freference\u002Fdata-model","4.reference\u002F1.data-model","i-lucide-database",{"title":61,"path":62,"stem":63,"icon":64},"API routes","\u002Freference\u002Fapi","4.reference\u002F2.api","i-lucide-route",{"title":66,"path":67,"stem":68,"icon":69},"Environment variables","\u002Freference\u002Fenv","4.reference\u002F3.env","i-lucide-settings-2",{"title":71,"path":72,"stem":73,"icon":74},"Security","\u002Freference\u002Fsecurity","4.reference\u002F4.security","i-lucide-shield",{"title":76,"path":77,"stem":78,"icon":79},"About","\u002Fabout","5.about","i-lucide-info",{"id":81,"title":66,"body":82,"description":751,"extension":752,"meta":753,"navigation":754,"path":67,"seo":755,"stem":68,"__hash__":756},"docs\u002F4.reference\u002F3.env.md",{"type":83,"value":84,"toc":745},"minimark",[85,90,99,519,536,544,563,612,622,647,651],[86,87,89],"h2",{"id":88},"variables","Variables",[91,92,93,94,98],"p",{},"Two are required; everything else has a working default. ",[95,96,97],"code",{},"NUXT_PUBLIC_*"," variables are readable by the browser — that is deliberate, they only carry flags the UI needs.",[100,101,102,121],"table",{},[103,104,105],"thead",{},[106,107,108,112,115,118],"tr",{},[109,110,111],"th",{},"Variable",[109,113,114],{},"Required",[109,116,117],{},"Default",[109,119,120],{},"Description",[122,123,124,144,162,188,205,238,259,282,304,325,339,353,371,391,415,431,449,466,483,499],"tbody",{},[106,125,126,132,135,138],{},[127,128,129],"td",{},[95,130,131],{},"NUXT_DATABASE_URL",[127,133,134],{},"yes",[127,136,137],{},"—",[127,139,140,141],{},"Postgres connection string, e.g. ",[95,142,143],{},"postgresql:\u002F\u002Ftick:password@localhost:5432\u002Ftick",[106,145,146,151,153,155],{},[127,147,148],{},[95,149,150],{},"NUXT_SESSION_PASSWORD",[127,152,134],{},[127,154,137],{},[127,156,157,158,161],{},"Session cookie encryption secret, 32+ chars. Generate: ",[95,159,160],{},"openssl rand -base64 36",". Changing it logs everyone out",[106,163,164,169,172,177],{},[127,165,166],{},[95,167,168],{},"NUXT_AUTO_MIGRATE",[127,170,171],{},"no",[127,173,174],{},[95,175,176],{},"true",[127,178,179,180,183,184,187],{},"Apply pending SQL migrations on production startup — requests get 503 until they finish, and a failure exits the process; ",[95,181,182],{},"false"," to run them out-of-band (",[95,185,186],{},"npx drizzle-kit migrate",")",[106,189,190,195,197,202],{},[127,191,192],{},[95,193,194],{},"NUXT_MIGRATIONS_DIR",[127,196,171],{},[127,198,199],{},[95,200,201],{},"\u003Ccwd>\u002Fserver\u002Fdb\u002Fmigrations",[127,203,204],{},"Override the migrations folder location",[106,206,207,212,214,219],{},[127,208,209],{},[95,210,211],{},"NUXT_PUBLIC_REGISTRATION",[127,213,171],{},[127,215,216],{},[95,217,218],{},"open",[127,220,221,222,224,225,228,229,232,233],{},"Who may sign up themselves: ",[95,223,218],{}," (anyone), ",[95,226,227],{},"invite"," (a valid invite link is required), ",[95,230,231],{},"closed"," (nobody). See ",[234,235,237],"a",{"href":236},"\u002Finstallation#registration-modes","registration modes",[106,239,240,245,247,252],{},[127,241,242],{},[95,243,244],{},"NUXT_AUTH_RATE_LIMIT",[127,246,171],{},[127,248,249],{},[95,250,251],{},"10",[127,253,254,255,258],{},"Max POSTs per IP per minute to each auth endpoint (login\u002Fregister\u002Fforgot\u002Freset); ",[95,256,257],{},"0"," disables it (e.g. when the reverse proxy rate-limits)",[106,260,261,266,268,272],{},[127,262,263],{},[95,264,265],{},"NUXT_SESSION_COOKIE_SECURE",[127,267,171],{},[127,269,270],{},[95,271,176],{},[127,273,274,275,277,278,281],{},"Secure flag on the session cookie. ",[95,276,182],{}," only for plain-HTTP access on a trusted network (see ",[234,279,71],{"href":280},"\u002Freference\u002Fsecurity#plain-http-and-the-secure-flag","). Empty or unrecognized values stop startup",[106,283,284,289,291,293],{},[127,285,286],{},[95,287,288],{},"NUXT_SMTP_HOST",[127,290,171],{},[127,292,137],{},[127,294,295,296],{},"SMTP server for outgoing mail. ",[297,298,299,300,303],"strong",{},"Mail is off until this and ",[95,301,302],{},"NUXT_MAIL_FROM"," are both set",[106,305,306,311,313,322],{},[127,307,308],{},[95,309,310],{},"NUXT_SMTP_PORT",[127,312,171],{},[127,314,315,318,319],{},[95,316,317],{},"465"," when secure, else ",[95,320,321],{},"587",[127,323,324],{},"SMTP port",[106,326,327,332,334,336],{},[127,328,329],{},[95,330,331],{},"NUXT_SMTP_USER",[127,333,171],{},[127,335,137],{},[127,337,338],{},"SMTP username; when unset the connection is unauthenticated",[106,340,341,346,348,350],{},[127,342,343],{},[95,344,345],{},"NUXT_SMTP_PASS",[127,347,171],{},[127,349,137],{},[127,351,352],{},"SMTP password \u002F app password",[106,354,355,360,362,366],{},[127,356,357],{},[95,358,359],{},"NUXT_SMTP_SECURE",[127,361,171],{},[127,363,364],{},[95,365,182],{},[127,367,368,370],{},[95,369,176],{}," = implicit TLS (usually port 465). Leave unset for STARTTLS on 587",[106,372,373,377,379,381],{},[127,374,375],{},[95,376,302],{},[127,378,171],{},[127,380,137],{},[127,382,383,384,387,388,390],{},"From address, e.g. ",[95,385,386],{},"Tick \u003Ctick@example.com>",". Required together with ",[95,389,288],{}," for any mail to go out",[106,392,393,398,400,404],{},[127,394,395],{},[95,396,397],{},"NUXT_DEMO_MODE",[127,399,171],{},[127,401,402],{},[95,403,182],{},[127,405,406,407,410,411,414],{},"Public-demo mode: ",[297,408,409],{},"wipes and reseeds the demo org"," (see the warning below) and returns ",[95,412,413],{},"403"," for destructive operations — password change, org rename, member removal, invite create\u002Frevoke, CSV import commit (preview still works) and permanent trash deletion",[106,416,417,422,424,428],{},[127,418,419],{},[95,420,421],{},"NUXT_PUBLIC_DEMO_MODE",[127,423,171],{},[127,425,426],{},[95,427,182],{},[127,429,430],{},"Shows the dismissible \"Demo — data resets nightly\" banner in the app shell",[106,432,433,438,440,446],{},[127,434,435],{},[95,436,437],{},"NITRO_HOST",[127,439,171],{},[127,441,442,445],{},[95,443,444],{},"0.0.0.0"," (image)",[127,447,448],{},"Bind address of the production server",[106,450,451,456,458,463],{},[127,452,453],{},[95,454,455],{},"NITRO_PORT",[127,457,171],{},[127,459,460],{},[95,461,462],{},"3000",[127,464,465],{},"Port of the production server",[106,467,468,473,475,480],{},[127,469,470],{},[95,471,472],{},"POSTGRES_PASSWORD",[127,474,171],{},[127,476,477],{},[95,478,479],{},"tick",[127,481,482],{},"docker-compose only: password for the bundled Postgres",[106,484,485,490,492,496],{},[127,486,487],{},[95,488,489],{},"TICK_PORT",[127,491,171],{},[127,493,494],{},[95,495,462],{},[127,497,498],{},"docker-compose only: host port the app is published on",[106,500,501,506,508,512],{},[127,502,503],{},[95,504,505],{},"TICK_IP",[127,507,171],{},[127,509,510],{},[95,511,444],{},[127,513,514,515,518],{},"docker-compose only: host IP the published port binds to — ",[95,516,517],{},"127.0.0.1"," behind a same-host reverse proxy (Docker bypasses ufw\u002Ffirewalld)",[520,521,522],"warning",{},[91,523,524,527,528,535],{},[95,525,526],{},"NUXT_DEMO_MODE=true"," is for throwaway demo instances only. On boot — and then hourly, whenever the last reset is over 24 hours old — it ",[297,529,530,531,534],{},"deletes the \"Hollow Studio\" org and the ",[95,532,533],{},"mara@example.com"," user, cascading away their clients, projects, tasks, tags and entries",", then reseeds three weeks of fake history. Never point a demo-mode instance at a database that holds real data.",[520,537,538],{},[91,539,540,541,543],{},"Keep ",[95,542,150],{}," stable across restarts and deploys — it encrypts every session cookie. A changed value logs everyone out.",[520,545,546],{},[91,547,548,550,551,553,554,556,557,553,559,562],{},[95,549,265],{},": ",[95,552,182],{},"\u002F",[95,555,257],{}," disable the Secure flag, ",[95,558,176],{},[95,560,561],{},"1"," keep it; an empty value or an unrecognized value aborts startup rather than guessing. Behind a TLS proxy leave it unset.",[564,565,566,598],"note",{},[91,567,568,571,572,575,576,579,580,582,583,585,586,589,590,593,594,597],{},[95,569,570],{},"docker-compose.yml"," passes the database, session, session-cookie, migration, registration, rate-limit and SMTP variables from ",[95,573,574],{},".env"," into the app container. ",[297,577,578],{},"Demo mode is deliberately left out"," — it wipes data, so add ",[95,581,397],{}," \u002F ",[95,584,421],{}," to the ",[95,587,588],{},"app"," service's ",[95,591,592],{},"environment:"," block by hand, and only on a throwaway instance. Any other variable needs the same treatment. Plain ",[95,595,596],{},"docker run -e …",", PM2 and systemd read variables directly.",[91,599,600,601,604,605,607,608,611],{},"Compose expands ",[95,602,603],{},"$"," inside ",[95,606,574],{}," values, so single-quote any secret that contains one: ",[95,609,610],{},"NUXT_SMTP_PASS='pa$$word'",".",[91,613,614,617,618,621],{},[95,615,616],{},".env.example"," at the repo root documents the same set; ",[95,619,620],{},"cp .env.example .env"," is the starting point for both Docker Compose and development.",[564,623,624],{},[91,625,626,627,630,631,634,635,638,639,642,643,646],{},"Request bodies to ",[95,628,629],{},"\u002Fapi\u002F*"," are capped at 1 MB (",[95,632,633],{},"413"," beyond it, ",[95,636,637],{},"411"," if a mutation arrives without a ",[95,640,641],{},"Content-Length","); the CSV import routes have their own 5 MB cap. Neither is configurable — raise the proxy's own ",[95,644,645],{},"client_max_body_size"," to match if you front Tick with nginx or Caddy.",[86,648,650],{"id":649},"npm-scripts","npm scripts",[100,652,653,663],{},[103,654,655],{},[106,656,657,660],{},[109,658,659],{},"Script",[109,661,662],{},"What it does",[122,664,665,675,688,701,711,725],{},[106,666,667,672],{},[127,668,669],{},[95,670,671],{},"npm run dev",[127,673,674],{},"Dev server with hot reload",[106,676,677,682],{},[127,678,679],{},[95,680,681],{},"npm run build",[127,683,684,685],{},"Production build to ",[95,686,687],{},".output\u002F",[106,689,690,695],{},[127,691,692],{},[95,693,694],{},"npm run typecheck",[127,696,697,700],{},[95,698,699],{},"vue-tsc"," over app + server",[106,702,703,708],{},[127,704,705],{},[95,706,707],{},"npm run db:push",[127,709,710],{},"Sync the Drizzle schema straight to the DB (development)",[106,712,713,718],{},[127,714,715],{},[95,716,717],{},"npm run db:generate",[127,719,720,721,724],{},"Write a SQL migration from schema changes — commit the ",[95,722,723],{},"server\u002Fdb\u002Fmigrations"," output",[106,726,727,732],{},[127,728,729],{},[95,730,731],{},"npm run db:seed",[127,733,734,735,550,739,582,741,744],{},"Demo org \"Hollow Studio\" with ~3 weeks of entries (",[234,736,738],{"href":737},"\u002Fgetting-started#demo-data","login",[95,740,533],{},[95,742,743],{},"tick-demo","). Re-running it wipes and recreates that org",{"title":746,"searchDepth":747,"depth":747,"links":748},"",2,[749,750],{"id":88,"depth":747,"text":89},{"id":649,"depth":747,"text":650},"Every variable Tick reads, plus the npm scripts you'll use in development.","md",{},{"icon":69},{"title":66,"description":751},"c6MMjzjxKia6qWaZHx4zJEpAsv4yf6JUM2AEvcXC3EE",[758,760],{"title":61,"path":62,"stem":63,"description":759,"icon":64,"children":-1},"Every server route under \u002Fapi — JSON, zod-validated, session-guarded.",{"title":71,"path":72,"stem":73,"description":761,"icon":74,"children":-1},"What Tick does to protect an instance, and what you as the operator still have to do.",1790777312464]