Arquivos estáticos
CSS, imagens, downloads e a travessia de diretório que o Kiln recusa.
Servir uma pasta#
server site on 8080:
assets "/static" from "./www"
route GET "/":
respond html "<link rel=stylesheet href=/static/estilo.css>…"/static/estilo.css passa a servir ./www/estilo.css. Uma pasta responde com o index.html de dentro dela, e o Content-Type sai do nome do arquivo.
`../` não escapa#
GET /static/estilo.css → 200
GET /static/../../../etc/passwd → 403
GET /static/%2e%2e/%2e%2e/etc/passwd → 403O caminho é resolvido e comparado com a pasta declarada antes de qualquer arquivo ser aberto. Essa é a falha clássica de servidor de arquivos, e vale saber que ela está fechada.
Ainda assim: sirva só o que é público. O .git e o .env moram no mesmo disco, e uma pasta declarada larga demais entrega os dois de forma perfeitamente legítima.
Caminho relativo a quê?#
adopt Arcane.OS as OS
server site on 8080:
// relativo a de onde o usuário chamou — frágil
assets "/static" from "./www"
// relativo ao programa — sempre certo
assets "/static" from OS.beside("../www")OS.beside resolve a partir do arquivo .df em execução. Sem isso, rodar dataforge run src/main.df de duas pastas diferentes carrega — ou não carrega — arquivos diferentes, e o erro só aparece na máquina de outra pessoa.
Um arquivo específico#
route GET "/manual.pdf":
respond file "./docs/manual.pdf"
// forçando o download, com outro nome
route GET "/dados":
respond Kiln.file("/tmp/export.csv", void, "dados-2026.csv")Gerado na hora#
route GET "/relatorio.xlsx":
livro := Xls.new()
Xls.sheet(livro, "Estoque", produtos_agora())
Xls.save(livro, "/tmp/r.xlsx")
respond file "/tmp/r.xlsx"O arquivo não precisa existir antes do pedido. É assim que a loja-web entrega uma planilha com os dados do momento, fórmulas inclusive.
Cache#
Arquivos estáticos saem com Cache-Control: public, max-age=3600. Uma hora é curta o bastante para não atrapalhar durante o desenvolvimento e longa o bastante para valer a pena. Se você versiona os nomes (estilo.a1b2.css), sirva com um Kiln.header mais generoso.
Limite de corpo#
Kiln.config(app, "limite_corpo", 5 * 1024 * 1024) // 5 MBO padrão é 10 MB, e um corpo maior é recusado com 413 antes de ser lido inteiro na memória. Sem esse limite, um POST de 2 GB derruba o processo — e ninguém precisa de permissão para tentar.