Skip to content
lantern.

Forms and masked input.

Collect input, validate it on the server, and return a useful response.

On this page

A complete greeting form.

On GET /, the handler returns a form. Activating its submit control sends POST /greet with ctx.fields.name. The handler validates the field and returns a personalized page. Paste the whole example into app.lua, then validate and preview.

app.lua
return function(ctx)  local P = ctx.page  if ctx.method == "POST" and ctx.path == "/greet" then    local name = ctx.fields.name    if type(name) ~= "string" or not name:match("%S") or #name > 60 then      return {kind="error", status=400, message="Enter a name, up to 60 characters."}    end    return {kind="page", page=P.document("Welcome", {      P.heading{text="Hello, " .. name .. "!"},      P.link{text="Try again", href="/"}    })}  end  if ctx.method ~= "GET" or ctx.path ~= "/" then    return {kind="error", status=404, message="Page not found"}  end  return {kind="page", page=P.document("Say hello", {    P.form{action="/greet", children={      P.input{name="name", text="Your name"},      P.submit{text="Say hello"}    }}  })}end

Field behavior.

Give each field in a form a distinct name using letters, digits, underscores or hyphens (up to 40 characters). Put all field controls inside form.children. Nested forms are invalid. A form can submit only to a path on its own site.

Use Tab and Enter, or click a control. Input opens a text prompt, checkboxes toggle, and select controls choose an option. Navigating clears form values. Always validate incoming values: another client can submit fields without using your form.

ControlSubmitted valueInitial value
inputStringEmpty string
checkboxBooleanfalse
selectSelected stringFirst option
submitNo named valueNot applicable

Password masking is not authentication.

password=true hides typing and the displayed value in an updated Lantern browser. The site operator still receives the field. Masking does not create users, sessions, password hashing, authorization or a password-reset flow. Never ask visitors to reuse real passwords in a demo.

The official Observatory uses the public code glow. It checks that code and returns a response without saving or echoing it. This demonstrates conditional server logic, not a private account system.

Inside a form
P.input{name="code", text="Demo code", password=true}

Retries and side effects.

The Hub records request IDs to prevent silent repeated execution. Reusing an ID with different content is rejected. A request that is still running or has failed is not automatically rerun. If a submission fails, check the current state before asking someone to submit it again.

Do not use ctx.requestId as proof of identity. Requests do not include a built-in authenticated site visitor. The Hub creator’s GitHub session is separate from visitors to your Lantern pages.

Found a mismatch? Include the exact error, runtime version and a small example with secrets removed.