ArtifactSmith

Servidor MCP de código aberto (MIT) que transforma a saída de agentes em páginas web, PDFs, documentos Word e planilhas que você pode compartilhar e revogar. Funciona com qualquer endpoint de modelo compatível com OpenAI. Auto-hospedado com Docker.

Documentação

ArtifactSmith

ArtifactSmith é um servidor de artefatos de código aberto para agentes de IA.

ArtifactSmith building the same page in five styles

Agentes o chamam para transformar seu trabalho em documentos: páginas web, PDFs, arquivos Word, planilhas e Markdown. Cada artefato é versionado e permanece privado até que alguém o compartilhe, e um link compartilhado pode ser revogado a qualquer momento.

É para pessoas que executam seus próprios agentes e querem artefatos como os do Muse em seus próprios servidores.

Experimente antes de instalar: https://artifactsmith.neuralami.ai/try. Eu executo uma cópia para a qual você pode apontar seu cliente MCP (envie um e-mail para obter um token, limitado a 20 builds, roda no meu servidor).

Exemplos, sem cadastro: página de comparação · página de status com gráfico · checklist em PDF

O que faz

ArtifactSmith dá aos agentes ferramentas para criar, editar, exportar (PDF, Word, Excel, Markdown), compartilhar e revogar documentos. Funciona com qualquer endpoint de modelo compatível com OpenAI. A ideia vem dos artefatos Muse da Meta.

An example artifact: a one-page decision brief with a recommendation, a status scorecard and a bar chart

Requisitos: Docker Engine com o plugin Compose. Uma chave de modelo (API compatível com OpenAI ou endpoint local) é necessária para construir páginas; publicar Markdown ou HTML já pronto funciona sem uma.

Início rápido

cp .env.example .env
# Set OPENAI_API_KEY (or AM_LLM_KEY). Change AM_LLM_BASE / AM_DEFAULT_MODEL
# only if you are not using the default OpenAI host and model.
# Object-store keys: leave them empty and run ./scripts/ensure-local-env.sh,
# or paste your own AM_STORE_KEY and AM_STORE_SECRET.
./scripts/ensure-local-env.sh
docker compose up -d --build

# Create a token (prints the raw value once; only its hash is stored).
# --workspace is a label that scopes every artifact the token can see
# (e.g. alpha, team-a). Tokens cannot cross workspaces.
docker compose exec server artifactsmith token add --name agent --workspace alpha

publish armazena uma página que você já escreveu e não chama o modelo, então o próximo passo funciona quando a chave está vazia. Cole o token impresso acima e execute isto. Ele imprime uma URL de compartilhamento.

export AM_TOKEN='paste-the-token'
docker compose exec -T -e AM_TOKEN server python - <<'PY'
import asyncio
import json
import os
from datetime import timedelta

from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

URL = "http://127.0.0.1:8780/mcp"
PAGE = """# Pilot store brief

The pilot store code is HARBOR-17. It is the identifier used on inventory, support, and rollout reports.
"""


def parse(result):
    if result.structuredContent is not None:
        return result.structuredContent
    return json.loads(result.content[0].text)


async def main():
    headers = {"Authorization": "Bearer " + os.environ["AM_TOKEN"]}
    async with streamablehttp_client(URL, headers=headers) as (read, write, _session):
        async with ClientSession(read, write) as session:
            await session.initialize()
            created = parse(
                await session.call_tool(
                    "publish",
                    {"display_name": "Pilot store brief", "markdown": PAGE},
                    read_timeout_seconds=timedelta(seconds=120),
                )
            )
            done = parse(
                await session.call_tool(
                    "status",
                    {"job_id": created["job_id"], "wait": 90},
                    read_timeout_seconds=timedelta(seconds=120),
                )
            )
            if done.get("status") != "done":
                raise SystemExit(json.dumps(done))
            shared = parse(
                await session.call_tool(
                    "share",
                    {"artifact_id": created["artifact_id"]},
                    read_timeout_seconds=timedelta(seconds=120),
                )
            )
            print(shared["url"])


asyncio.run(main())
PY

O servidor lê AM_LLM_BASE, AM_LLM_KEY ou OPENAI_API_KEY, e AM_DEFAULT_MODEL de .env. A base padrão é https://api.openai.com. Uma barra final /v1 na base é aceitável; o cliente anexa /v1/chat/completions por conta própria. create precisa de uma chave ou de um endpoint local compatível com OpenAI. Com uma chave vazia, um create com falha relata que OPENAI_API_KEY / AM_LLM_KEY está vazio. Execute docker compose up -d novamente para que a alteração tenha efeito.

As portas do host vinculam-se a 127.0.0.1:

  • http://127.0.0.1:8780/mcp é a API MCP (token bearer obrigatório)
  • http://127.0.0.1:8780/app/ é Minhas páginas (cole o mesmo token; o navegador recebe um cookie de sessão)
  • http://127.0.0.1:8781/ é a origem de pré-visualização e compartilhamento sem cookies (/p/…, /s/…)

Usar a imagem publicada

ghcr.io/vrijsinghani/artifactsmith:0.1.0 é publicada para linux/amd64 em cada lançamento. Compilar a partir do código-fonte (as etapas do Compose acima) continua sendo o padrão.

Conectar um cliente

Claude.ai: Personalizar → Conectores → + Adicionar → Adicionar conector personalizado. Cole https://artifactsmith.neuralami.ai/mcp,, selecione Conectar e faça login com seu e-mail. O conector então funciona também nos aplicativos de desktop e celular do Claude. Planos gratuitos permitem um conector personalizado. Nos planos Team e Enterprise, um proprietário o adiciona primeiro.

github.com/vrijsinghani/Artifactsmith é a cópia que você executa por conta própria. Claude Desktop, Claude Code e Cursor usam um token lá. O endpoint MCP nesta máquina é http://127.0.0.1:8780/mcp. Envie o token de artifactsmith token add como Authorization: Bearer YOUR_TOKEN. O valor do cabeçalho começa com Bearer .

ChatGPT Free, Go e Plus não podem adicionar um servidor MCP personalizado. Pro pode adicionar um com acesso somente leitura e busca, portanto não pode construir páginas. Um administrador do ChatGPT Business, Enterprise ou Edu pode adicioná-lo na web; ele faz login com OAuth.

Claude Desktop armazena a configuração em claude_desktop_config.json (Configurações → Desenvolvedor → Editar Config). O trecho executa npx e mcp-remote, o que requer Node.js. Reinicie o Claude Desktop após salvar.

{
  "mcpServers": {
    "artifactsmith": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://127.0.0.1:8780/mcp", "--header", "Authorization:${AUTH_HEADER}"],
      "env": { "AUTH_HEADER": "Bearer YOUR_TOKEN" }
    }
  }
}

Claude Code armazena .mcp.json, ou execute:

claude mcp add --transport http artifactsmith http://127.0.0.1:8780/mcp --header "Authorization: Bearer YOUR_TOKEN"
{
  "mcpServers": {
    "artifactsmith": {
      "type": "http",
      "url": "http://127.0.0.1:8780/mcp",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}

Cursor armazena ~/.cursor/mcp.json, ou .cursor/mcp.json no projeto.

{
  "mcpServers": {
    "artifactsmith": {
      "url": "http://127.0.0.1:8780/mcp",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}

Exemplo de prompt: Crie um briefing de uma página chamado Briefing da loja piloto. Passe isto como source_content: O código da loja piloto é HARBOR-17. É o identificador usado nos relatórios de inventário, suporte e implantação.

artifactsmith stdio pode listar ferramentas em stdin e stdout e não escuta. Não pode usar o armazenamento de objetos ou o modelo, portanto não pode construir ou publicar uma página.

Servindo na sua rede ou atrás de um proxy

O Compose publica API (8780) e pré-visualização/compartilhamento (8781) em AM_BIND_ADDRESS (padrão 127.0.0.1). Cartões e links usam AM_API_URL, AM_PREVIEW_URL e AM_SHARE_URL (AM_SHARE_URL vazio recai em AM_PREVIEW_URL). Expor as portas sem TLS coloca tokens bearer e conteúdo de artefatos na rede em texto claro.

Acesso LAN

Vincule em todas as interfaces (ou em um IP de NIC específico) e aponte as variáveis de URL pública para o endereço que os clientes abrirão:

# .env  (192.0.2.10 is documentation-range; substitute your LAN IP)
AM_BIND_ADDRESS=0.0.0.0
AM_API_URL=http://192.0.2.10:8780
AM_PREVIEW_URL=http://192.0.2.10:8781
AM_SHARE_URL=http://192.0.2.10:8781
AM_ALLOWED_HOSTS=192.0.2.10:8780,127.0.0.1:8780,localhost:8780

Então docker compose up -d --build. Clientes abrem http://192.0.2.10:8780/mcp e links de pré-visualização/compartilhamento usam 192.0.2.10, não 127.0.0.1. Clientes MCP baseados em navegador também precisam de AM_ALLOWED_ORIGINS definido para a origem da página.

URLs de compartilhamento e pré-visualização são construídas a partir de AM_SHARE_URL / AM_PREVIEW_URL quando o servidor as retorna. Links que você copiou antes de mudar para servir em LAN ainda usam o host base antigo; chame share novamente (ou copie a URL de inspect / status) após alterar essas configurações.

Proxy reverso com TLS

Mantenha o vínculo do host em loopback e encerre TLS no nginx ou Caddy. Defina as URLs públicas e listas de permissão para o hostname externo:

# .env
AM_BIND_ADDRESS=127.0.0.1
AM_API_URL=https://artifacts.example.com
AM_PREVIEW_URL=https://preview.artifacts.example.com
AM_SHARE_URL=https://preview.artifacts.example.com
AM_ALLOWED_HOSTS=artifacts.example.com
AM_ALLOWED_ORIGINS=https://artifacts.example.com

Exemplo mínimo de Caddy (API na 443, pré-visualização em um segundo hostname):

artifacts.example.com {
  reverse_proxy 127.0.0.1:8780
}
preview.artifacts.example.com {
  reverse_proxy 127.0.0.1:8781
}

Trecho mínimo de nginx para o host da API:

server {
  listen 443 ssl;
  server_name artifacts.example.com;
  # ssl_certificate / ssl_certificate_key omitted
  location / {
    proxy_pass http://127.0.0.1:8780;
    proxy_set_header Host $host;
    proxy_set_header Authorization $http_authorization;
    # status wait can be up to 90s; nginx defaults cut at 60s.
    proxy_read_timeout 120s;
    proxy_send_timeout 120s;
  }
}

Quando AM_ALLOWED_HOSTS lista apenas o hostname público, um curl http://127.0.0.1:8780/mcp local recebe HTTP 421 (proteção contra rebinding de DNS). Aponte os clientes para o hostname com proxy. Clientes MCP baseados em navegador enviam um cabeçalho Origin; defina AM_ALLOWED_ORIGINS para essa origem ou a solicitação será rejeitada.

Cliente MCP em outra máquina

Aponte o cliente para {AM_API_URL}/mcp e envie o token como Authorization: Bearer <token> (o valor impresso uma vez por artifactsmith token add). O valor do cabeçalho começa com Bearer . artifactsmith stdio pode listar ferramentas em stdin e stdout e não escuta. Não pode usar o armazenamento de objetos ou o modelo, portanto não pode construir ou publicar uma página. Exemplo:

URL:    http://192.0.2.10:8780/mcp
Header: Authorization: Bearer asmb_…

Como funciona

Agentes falam com ArtifactSmith via MCP. Eles passam a solicitação do usuário como verbatim_request e qualquer pesquisa como source_content ou source_files. create inicia um build privado e não publica um link de compartilhamento. Quer uma página estilizada? Passe Markdown para publish e ele é estilizado automaticamente (omita format quando você passar markdown e nenhum html; estilo padrão da casa, aplicado quando a página é aberta). Você ainda pode passar format=markdown. Ou chame create com source_content. HTML bruto em publish é apenas para uma página que você mesmo projetou, e é mostrado exatamente como fornecido.

O servidor chama um endpoint compatível com OpenAI (até duas tentativas quando as verificações falham). O modelo escreve o conteúdo. Um renderizador fixo transforma isso em HTML, Markdown, PDF, DOCX ou XLSX. publish pula o modelo e executa as mesmas verificações e o mesmo renderizador. Chame status (ou wait até 90 segundos) para obter uma pré-visualização privada.

share cria um link público para uma versão exata. unshare faz esse link retornar 404. Tokens pertencem a um workspace e carregam um conjunto de permissões.

Documentos podem citar fontes públicas http(s) como links clicáveis. O servidor remove scripts, rejeita hosts de links privados e prováveis segredos, e mantém imagens, CSS, fontes e iframes autocontidos para que um download abra offline e não faça chamadas externas. Limites de tamanho se aplicam. O processo não envia dados de uso para lugar nenhum.

Formatos de saída

Formatocreate(..., format=)Arquivo principalRenderizador
HTMLhtml (padrão)index.htmlsanitizar, depois armazenar
Markdownmarkdowndocument.mdtexto UTF-8
PDFpdfdocument.pdfWeasyPrint (HTML/CSS para PDF)
DOCXdocxdocument.docxpython-docx
XLSXxlsxdocument.xlsxopenpyxl

HTML usa um estilo visual (padrão da casa) e armazena uma página completa. Os outros formatos pedem Markdown ao modelo, e então um renderizador o converte. publish aceita o HTML ou Markdown finalizado e usa esse mesmo renderizador. PDF, DOCX e XLSX também armazenam content.md para que edições posteriores tenham uma base de texto.

PDF passa por WeasyPrint, que precisa de Pango e Cairo. A imagem Docker não inclui Chromium. Detalhes estão em docs/formats.md.

Estilos

create e edit aceitam um style opcional para HTML: house, bold, editorial, playful, terminal ou swiss. O padrão é AM_DEFAULT_STYLE (geralmente house). Em markdown, PDF, DOCX e XLSX, o prompt do modelo não usa esse estilo visual. Uma página markdown publicada ainda é mostrada no estilo registrado quando aberta. Omita style em edit para manter o último estilo.

create também aceita mode: full (padrão, AM_BUILD_MODE) faz o modelo escrever HTML. fast faz o modelo escrever Markdown e o servidor renderizar esse estilo. fast é apenas um primeiro build em HTML. Edições e outros formatos permanecem no caminho completo. AM_FAST_STYLES (vazio por padrão) lista estilos cujo mode omitido é fast; um mode explícito ainda vence. Um documento HTML completo na fonte, ou uma solicitação para publicar esse HTML como está, permanece em full quando mode é omitido. Mencionar HTML junto de "inalterado" não conta.

  • house: a página tranquila de hoje — branco, um acento, ousadia em um só lugar.
  • bold: hero escuro de navy a violeta, título branco enorme, cartões e chips saturados.

Desktop and phone preview of the bold HTML style: a dark gradient hero with a huge headline, then colored step cards

  • editorial: matéria de revista — papel off-white, título serifado grande, linhas finas, um acento vermelho profundo.

Desktop and phone preview of the editorial HTML style: a large serif headline, a red-ruled lead, and two columns

  • playful: página creme, cartões arredondados com cores de bala, contornos grossos e sombras deslocadas.

Desktop and phone preview of the playful HTML style: a coral rounded banner, googly-eye accents, and mint chat cards

  • terminal: modo escuro, monoespaçado, verde neon e ciano, seções como janelas de terminal falsas.

Desktop and phone preview of the terminal HTML style: a green shell-prompt headline and dark windows with three title-bar dots

  • swiss: grade branca, tipografia grotesca preta enorme, laranja-sinal apenas em números e linhas.

Desktop and phone preview of the swiss HTML style: a huge black headline, an orange rule, and oversized orange numerals

Ferramentas MCP

FerramentaPermissãoO que faz
createcreateEnfileira um build privado. format, style e mode opcionais. Não cria link de compartilhamento.
publishpublishArmazena Markdown para obter uma página estilizada, ou armazena HTML que você mesmo projetou. Sem chamada de modelo.
editeditNova versão a partir da última versão concluída. Recusa um base_version desatualizado.
statusreadConsulta, ou wait (0 a 90 segundos), até concluído, falho ou needs_input.
list_artifactsreadCatálogo do workspace do token.
inspectreadManifesto, histórico, estado de compartilhamento. include_content opcional retorna o texto armazenado dessa versão, limitado ao limite de caracteres de publicação.
exportexportLink de download para o arquivo renderizado, válido por 15 minutos.
shareshareLink público para uma versão exata.
unshareshareRevoga o link público imediatamente.
revoke_previewsexportExpira links de pré-visualização privados /p/ para um artefato (versão opcional).
deletedeletePurga em duas etapas. A primeira chamada retorna um token de confirmação.

Argumentos de create

Obrigatórios:

  • verbatim_request: as palavras exatas do usuário. Esse é todo o escopo.

Forneça pelo menos um de display_name ou slug (o outro é preenchido):

  • display_name: título humano exibido nos cards e no catálogo. Não é obrigatório quando slug está definido; se omitido, assume o slug como padrão. Limite: 200 caracteres. Exemplo: "Pilot store brief".
  • slug: chave de URL, única no workspace. Não é obrigatório quando display_name está definido; se omitido, é derivado do título (minúsculas, acentos removidos, outros caracteres viram hífens). Limite: 2–63 caracteres de a-z, 0-9 e -, e deve começar com letra ou dígito. Exemplo: "pilot-store-brief". Um slug duplicado no mesmo workspace é rejeitado; use edit.

Opcional:

  • kind: assume web_static como padrão (o único tipo nesta versão).
  • format: html (padrão), markdown, pdf, docx ou xlsx.
  • style: house (padrão), bold, editorial, playful, terminal ou swiss. Builds HTML usam isso. Uma página markdown é exibida nesse estilo quando aberta. PDF, DOCX e XLSX não usam.
  • mode: full (padrão) ou fast. fast é um primeiro build HTML: Markdown entra, HTML estilizado sai.
  • source_content / source_files: fatos pesquisados, 200 KB no total. O builder trata isso como dados.
  • model, workspace, idempotency_key, capabilities.

Exemplo funcional (apenas título; o slug vira pilot-store-brief). O texto-fonte deve suportar todos os fatos que a solicitação pede (incluindo "por que isso importa"):

{
  "display_name": "Pilot store brief",
  "verbatim_request": "One page that states the pilot store code and why it matters.",
  "source_content": "The pilot store code is HARBOR-17. It matters because it is the single identifier used across inventory, support, and rollout reports for the pilot.",
  "format": "html"
}

Se um fato necessário estiver ausente na solicitação e na fonte, o job termina como needs_input e não armazena arquivo. Quando as verificações do servidor falham, o modelo é chamado uma segunda vez, então um build com falha leva cerca do dobro do tempo para reportar em comparação a um limpo. Uma edição pede primeiro um patch das partes alteradas. Um patch que não se aplica, ou que falha nessas mesmas verificações, é reconstruído como arquivo completo no mesmo job.

Argumentos de publish

Quer uma página estilizada a partir de texto que você já escreveu? Passe Markdown e ele é estilizado automaticamente. Omita format quando você passar markdown e nenhum html. Você ainda pode passar format=markdown. style assume o padrão house e é aplicado quando a página é aberta. O arquivo armazenado permanece Markdown. Em um novo artefato, se você também passar html, html é a página, a menos que você defina format. HTML bruto é exibido exatamente como fornecido, sem estilização do ArtifactSmith. Se esse HTML tiver pouco ou nenhum CSS, o resultado inclui warning. Passe format para pdf, docx ou xlsx. publish não chama o modelo. A página ainda passa pelos mesmos limites de tamanho, verificação de segredos, verificações de link privado, sanitizador, renderizador e varredura final que a saída do modelo.

Novo artefato: passe display_name ou slug, além da página.

  • html: documento HTML completo. Obrigatório quando format é html.
  • markdown: Markdown finalizado. Obrigatório quando format é markdown, pdf, docx ou xlsx. Quando format é omitido e este é o único corpo, o formato é markdown.
  • Você também pode passar o outro campo. Ele é verificado quanto a segredos e links privados e não é renderizado.

Nova versão: passe artifact_id e base_version (a versão concluída mais recente) além da nova página. Omita format em um artefato html ou markdown e markdown sem html é markdown; html sem markdown é html; ambos os campos mantêm o formato do artefato. Um artefato pdf, docx ou xlsx mantém esse formato quando format é omitido e a nova página é markdown. Passe format para escolher outro formato suportado. Passe display_name para renomear o card quando esta versão terminar. Omita display_name e um título de página alterado (o <title> HTML, ou o primeiro cabeçalho Markdown) vira o título do card. O id e o slug do artefato permanecem os mesmos. A página de compartilhamento mais recente usa esse nome para <title> e og:title. Versões mais antigas mantêm seu próprio formato. Uma página markdown compartilhada abre como HTML estilizado; ?raw=1 ou document.md nessa URL baixa o Markdown armazenado.

summary e assumptions são campos opcionais do card (8000 caracteres cada) e são verificados como saída do modelo. Um resumo ou suposição mais longo é rejeitado quando a chamada chega, com metadata exceeds N characters, e nada é armazenado. note é opcional. O Inspect o exibe ao lado de Published a finished page.. Uma nota longa demais, ou que falha na verificação de segredos, links privados ou lista de permissões, é rejeitada antes que uma linha de versão exista. Em markdown, style altera a página aberta (house por padrão). Em HTML, style é registrado e a página enviada não é reestilizada. PDF, DOCX e XLSX não usam a folha de estilo HTML.

Retorna artifact_id, version e job_id. Chame status até done ou failed. Uma página rejeitada é failed, e error nomeia a verificação que falhou. Quando o job termina, status e inspect incluem safety_changes, a lista de edições de segurança que alteraram a página enviada. A publicação HTML não transforma uma URL nua em link. publish não cria um link de compartilhamento. Isso conta para a cota de build por token e precisa da permissão publish.

export

Permissão: export.

Argumentos:

  • artifact_id (obrigatório): qual artefato baixar.
  • version (opcional): uma versão concluída. Omita para usar a versão concluída mais recente.

Retorna download_url (válido por 15 minutos), file, version, sha256, self_contained e expires_in ("15m").

{ "artifact_id": "art_example", "version": 1 }

share

Permissão: share.

Argumentos:

  • artifact_id (obrigatório): qual artefato publicar.
  • version (opcional): a versão concluída exata. Omita para usar a versão concluída mais recente.
  • ttl_days (opcional): vida útil em dias. Assume AM_SHARE_TTL_DAYS como padrão. Quando AM_SHARE_TTL_DAYS é 0, ttl_days é ignorado e o link dura até ser revogado. Caso contrário, a vida útil é limitada por AM_SHARE_TTL_MAX_DAYS.

Retorna state ("shared"), url, version, expires_at, lifetime e reused. Qualquer pessoa com a URL pode abrir essa versão.

{ "artifact_id": "art_example" }

unshare

Permissão: share.

Argumentos:

  • artifact_id (obrigatório): qual artefato revogar.
  • version (opcional): revoga uma versão. Omita, ou passe 0, para revogar todas as versões.

Retorna artifact_id, revoked_links (quantos links foram revogados) e state. state é "shared" quando um link público ainda está ativo, com url e follows ("latest" ou "pinned"). É "not_shared" apenas quando nenhum link público permanece. Revogar um pin deixa o link de seguir mais recente ativo. Uma URL revogada retorna 404 imediatamente.

{ "artifact_id": "art_example" }

delete

Permissão: delete.

Argumentos:

  • artifact_id (obrigatório): qual artefato purgar.
  • confirm_token (omitir na primeira chamada): token da primeira resposta. Expira em 10 minutos.

A primeira chamada não exclui nada. Ela retorna step ("confirm"), confirm_token, title, versions, expires_in ("10m") e um aviso. A segunda chamada purga todas as versões armazenadas, links de compartilhamento e linhas do banco de dados. Ela retorna deleted (true), artifact_id e purged_object_versions.

{ "artifact_id": "art_example" }
{ "artifact_id": "art_example", "confirm_token": "paste-the-token" }

Configuração

Cada configuração também tem uma variante NAME_FILE que lê o valor de um arquivo.

VariávelPadrãoFinalidade
OPENAI_API_KEY / AM_LLM_KEYvazioCredencial LLM para o endpoint configurado.
AM_LLM_APIchatchat (Chat Completions) ou responses.
AM_LLM_BASEhttps://api.openai.comRaiz do host compatível com OpenAI. Com ou sem /v1 final.
AM_LLM_TEMPERATUREnão definidoTemperatura do Chat Completions. Não definido envia 0; none omite o campo.
AM_DEFAULT_MODELgpt-4o-miniNome do modelo enviado ao LLM.
AM_DEFAULT_STYLEhouseEstilo visual HTML quando create omite style.
AM_BUILD_MODEfullfull ou fast quando create omite mode. fast é somente HTML do primeiro build.
AM_FAST_STYLESvazioEstilos separados por vírgula (house, bold, …) que usam fast por padrão quando create omite mode. Vazio mantém AM_BUILD_MODE para cada estilo. Um mode explícito ainda vence. Uma fonte HTML finalizada, ou um pedido para publicar esse HTML como está, permanece full quando mode é omitido. Mencionar HTML junto de "inalterado" não. Outros formatos e edições permanecem full. Um nome desconhecido falha na inicialização.
AM_SHARE_TTL_DAYS300 significa até ser revogado.
AM_SHARE_TTL_MAX_DAYS365Limite para TTLs positivos.
AM_STORE_ENDPOINT(obrigatório)Armazenamento de objetos compatível com S3.
AM_STORE_BUCKETartifactsNome do bucket.
AM_STORE_KEY / AM_STORE_SECRET(obrigatório)Credenciais do armazenamento de objetos.
AM_BLOCK_PRIVATE_LINKStrueRejeita hosts localhost, RFC1918 e link-local na saída.
AM_ALLOWED_LINK_DOMAINSvazioSe definido, todo host restante deve corresponder.
AM_MAX_OUTPUT_BYTES50 MiBLimite da saída renderizada.
AM_RENDER_TIMEOUT120Segundos para uma chamada de renderização.
AM_BUILD_TIMEOUT900Segundos para o trabalho inteiro.
AM_EDIT_PATCH_TIMEOUT_S45Segundos para a chamada do patch de edição. Um timeout volta para uma reescrita completa.
AM_MAX_BUILDS2Workers concorrentes. Um teste hospedado usa no máximo um desses slots.
AM_BUILDS_PER_HOUR20Cota por token para tokens internos. Tokens de teste usam AM_TRIAL_BUILDS_PER_HOUR.
AM_TRIAL_ENABLEDfalseAbre a página hospedada de experimente (/try, antigo /trial) e permite seus builds. false fecha ambos imediatamente.
AM_TRIAL_EMAIL_VERIFYtrueEnvia por e-mail um link de uso único antes de mostrar o token. false mostra no envio e registra o endereço como não verificado.
AM_TRIAL_BUILD_LIMIT20Builds vitalícios para um token de teste.
AM_TRIAL_FREE_NEEDS_INPUT5Trabalhos needs_input por token hospedado que não usam um build. Outros usam.
AM_TRIAL_BUILDS_PER_HOUR10Limite horário para um token de teste. Não altera AM_BUILDS_PER_HOUR.
AM_TRIAL_DAILY_USD10Limite global de gastos de teste por dia UTC. Tokens internos não são limitados.
AM_TRIAL_PRICE_IN / AM_TRIAL_PRICE_OUT0.10 / 0.50USD por 1M de tokens de entrada / saída, usado para precificar a retenção.
AM_TRIAL_MODELanthropic/claude-haiku-5-5Modelo para cada build de teste. O nome do modelo do chamador é ignorado.
AM_TRIAL_MAX_OUTPUT_TOKENS16000Limite de saída enviado nas chamadas de modelo de teste.
AM_TRIAL_SIGNUPS_PER_IP / AM_TRIAL_SIGNUP_WINDOW_S3 / 86400Tentativas de inscrição por endereço IPv4 ou /48 IPv6 por janela.
AM_TRIAL_SIGNUPS_PER_DAY300Inscrições emitidas por dia UTC. Depois disso, /try diz que não há mais tokens hospedados hoje, de volta às 00:00 UTC.
AM_TRIAL_LINK_TTL_S1800Vida útil do magic link, em segundos.
AM_TRIAL_RETENTION_DAYS30Exclui um workspace de teste este número de dias após seu último build.
AM_TRIAL_ABUSE_EMAILvazioMailto no rodapé da página de teste. Vazio omite o link.
AM_SMTP_HOST / AM_SMTP_PORT / AM_SMTP_FROMvazio / 587 / vazioE-mail do magic link. Host ou remetente vazio falha fechado.
AM_SMTP_USER / AM_SMTP_PASSWORDvazioLogin SMTP. Senha aceita AM_SMTP_PASSWORD_FILE.
AM_SMTP_TLStrueInicia TLS ao enviar o magic link.
AM_HOST127.0.0.1Endereço de escuta para artifactsmith serve. Compose/imagem definem 0.0.0.0 no contêiner.
AM_BIND_ADDRESS127.0.0.1Bind de publicação do host para as portas 8780 e 8781 em compose.yaml.
AM_API_PORT / AM_PREVIEW_PORT8780 / 8781Portas de escuta.
AM_API_URL / AM_PREVIEW_URL / AM_SHARE_URLhttp://127.0.0.1:8780 / :8781URLs escritas em cartões e links. AM_SHARE_URL vazio usa AM_PREVIEW_URL. Uma URL de API https:// marca o cookie Secure das páginas My.
AM_COOKIE_SECUREsegue AM_API_URLtrue ou false força o flag Secure do cookie das páginas My. Deixe não definido em HTTP local.
AM_TRUSTED_PROXIESvazioIPs ou CIDRs separados por vírgula que podem definir X-Real-IP ou X-Forwarded-For para limites de login das páginas My e limites de inscrição de teste. Vazio ignora esses cabeçalhos.
AM_LOGIN_LIMIT / AM_LOGIN_PREFIX_LIMIT / AM_LOGIN_WINDOW_S8 / 8 / 900Falhas de login por endereço IPv4 ou /64 IPv6, e por prefixo de token com hash, dentro da janela (segundos).
AM_ALLOWED_HOSTS127.0.0.1:<api_port>,localhost:<api_port>Valores de Host separados por vírgula para proteção contra rebinding de DNS do MCP (ativada por padrão).
AM_ALLOWED_ORIGINSvazioValores de Origin separados por vírgula para proteção contra rebinding de DNS do MCP.
AM_DATA_DIR / AM_SECRETS_DIR/data / /secretsSQLite e a chave de assinatura HMAC.

Copie .env.example para um arquivo local. ./scripts/ensure-local-env.sh preenche AM_STORE_KEY e AM_STORE_SECRET vazios. A imagem de armazenamento é rustfs/rustfs:1.0.1 (fixada por digest em compose.yaml).

Executando uma página hospedada de experimente

Defina AM_TRIAL_ENABLED=true e aponte AM_API_URL para a origem pública. Com AM_TRIAL_EMAIL_VERIFY deixado em true, /try envia por e-mail um link de uso único (AM_SMTP_HOST e AM_SMTP_FROM são obrigatórios; se qualquer um estiver vazio, a inscrição falha fechado). GET /trial redireciona para /try. POST /trial e /trial/verify permanecem para que um formulário aberto ou um link já enviado ainda funcione. O domínio Message-ID é o domínio de AM_SMTP_FROM. Abrir o link não o gasta. O botão "Mostrar meu token" mostra o token do workspace uma vez. Defina AM_TRIAL_EMAIL_VERIFY=false para mostrar essa mesma página no envio e não enviar e-mail; a linha de inscrição armazena verified=0. De qualquer forma, a página inclui configuração para Claude Desktop (mcp-remote), Claude Code (HTTP), Cursor e um cliente MCP genérico. O trecho do Desktop coloca o valor do bearer em AUTH_HEADER para que um espaço não seja um argumento. Cursor usa uma URL e cabeçalhos, sem argumentos de comando. Cada trecho diz YOUR_TOKEN. A página também nomeia a rota Customize → Connectors → Add custom connector do Claude Desktop, com essa mesma URL pública do MCP e um cabeçalho de solicitação Authorization. O valor do cabeçalho começa com Bearer. Ela nomeia claude_desktop_config.json, .mcp.json e ~/.cursor/mcp.json, o comando claude mcp add para essa URL pública do MCP e um prompt de exemplo que passa notas como source_content. A página declara o limite de builds de AM_TRIAL_BUILD_LIMIT. Quando AM_API_URL é uma origem https pública, essa mesma página carrega um cartão Open Graph e Twitter para {AM_API_URL}/try. A imagem é {AM_API_URL}/static/og-trial.png e não precisa de token. Um AM_API_URL loopback ou privado omite as tags. A página permanece noindex. Um endereço normalizado recebe um token. Endereços Gmail ignoram pontos e +tags. Domínios descartáveis são rejeitados. Tentativas de inscrição de uma rede IPv6 compartilham um /48. O login das páginas My permanece um /64. No máximo AM_TRIAL_SIGNUPS_PER_DAY tokens são emitidos por dia UTC (padrão 300). Depois disso, /try diz que não há mais tokens hospedados hoje, de volta às 00:00 UTC.

Builds hospedados sempre usam AM_TRIAL_MODEL e não podem chamar publish. A lista de ferramentas de um token hospedado omite publish. As respostas incluem builds_remaining. Até AM_TRIAL_FREE_NEEDS_INPUT (padrão 5) trabalhos needs_input por token não usam um build; depois disso, usam. Um trabalho com falha ainda usa. O limite diário de gastos ainda conta os tokens de modelo desse trabalho, e o limite horário ainda conta a tentativa. Excluir um artefato não devolve esses builds. Quando o limite diário de gastos é atingido, o erro é the hosted copy hit today's limit, back at 00:00 UTC. Um token esgotado aponta para o repositório GitHub. Links de compartilhamento para um token hospedado duram no máximo 30 dias. A página servida adiciona noindex mesmo quando a página já tem uma meta robots que não diz noindex, e um rodapé que linka o repositório e permanece visível se a página oculta rodapés. Workspaces hospedados ociosos são excluídos após AM_TRIAL_RETENTION_DAYS (padrão 30). Essa varredura roda na inicialização e depois a cada hora.

Segurança

Tokens são emitidos por workspace e armazenados como hashes SHA-256. Saber um id de artefato não concede acesso.

Permissões são create, read, edit, publish, export, share e delete.

Builds permanecem privados até você chamar share. Isso cria uma URL /s/… pública que segue a versão final mais recente. Passe version ou pin para congelar uma versão em sua própria URL. unshare faz retornar 404. unshare com uma versão revoga apenas esse pin.

As páginas de pré-visualização e compartilhamento rodam em sua própria porta, não enviam cookies e definem CSP script-src 'none' com sandbox allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation.

As páginas My (/app/ na origem da API) são um catálogo somente leitura para o workspace do token. O login cola um token em um formulário. A resposta define um cookie de sessão HttpOnly, SameSite=Strict no caminho /app e não coloca o token na URL. O cookie é Secure quando AM_API_URL é https (ou quando AM_COOKIE_SECURE=true). Cada solicitação carrega o token novamente, então uma revogação derruba a sessão imediatamente. O id de artefato de outro workspace é um 404. A lista mostra 50 páginas por vez. Os horários usam America/New_York, a menos que o visualizador defina outra zona IANA; a página nomeia essa zona. Atrás de um proxy reverso, defina AM_TRUSTED_PROXIES para esse proxy para que uma falha de login seja contada para o cliente (X-Real-IP, ou o salto X-Forwarded-For mais à direita) e para um hash do prefixo do token. Clientes IPv6 compartilham um /64. Cada endereço IPv4 é seu próprio bucket, incluindo um endereço IPv6 mapeado para IPv4. Outros pares não podem fornecer esses cabeçalhos. A origem de pré-visualização não serve /app/.

Scripts são removidos. Links públicos http(s) para hosts globais são mantidos (com rel="noopener noreferrer nofollow" e target="_blank" em HTML). Âncoras # na página permanecem na página. Verificações de host usam urllib mais uma aproximação estilo WHATWG; elas garantem que links aceitos não alcancem hosts privados, não que todo link aceito seja válido no navegador. Citações //host relativas a protocolo são atualizadas para https:// somente quando o host é público; destinos // privados ou perigosos são neutralizados, não atualizados. Hosts privados, loopback e link-local falham no build, assim como javascript:, vbscript:, data: e file:. Uma URL de imagem remota se torna um link clicável para essa URL (texto alternativo como rótulo) em todos os formatos — nada carrega a imagem ao abrir. CSS url(), fontes e iframes permanecem locais. Segredos falham no build. Limites de tamanho e tempo se aplicam.

O processo não envia dados de uso para lugar nenhum. docs/threat-model.md e SECURITY.md têm o restante.

Tokens

Um workspace é um namespace para artefatos (por exemplo, alpha ou team-a). Cada token pertence a exatamente um workspace e só pode criar ou ler artefatos nesse workspace. Saber o id de um artefato de outro workspace não concede acesso.

artifactsmith token add --name NAME --workspace WS [--perms create,read,edit,publish,export,share,delete]
artifactsmith token list
artifactsmith token revoke --id ID | --name NAME
artifactsmith token perms --id ID | --name NAME
artifactsmith token perms --name NAME --add publish
artifactsmith token perms --name NAME --remove publish

token perms altera uma permissão em um token existente. O valor do token permanece o mesmo. Cada adição ou remoção acrescenta uma linha ao log de auditoria. Listar permissões não acrescenta. Se o nome corresponder a mais de um token, passe --id.

Desinstalar / redefinir

# Stop containers, delete compose volumes, and remove images created for this project
# (server, storage-init, and the rustfs/rustfs pull). Requires store keys in .env like other compose commands.
docker compose down --volumes --rmi all

Copie .env.example novamente e execute o quickstart para começar do zero.

Desenvolvimento

python3 -m venv .venv && source .venv/bin/activate
make install
make ci          # lint, types, unit tests, denylist

Executando os testes

make test executa a suíte de testes unitários com o mesmo comando pytest que o CI usa. Ele não inicia o Docker.

Os testes de ponta a ponta usam compose.test.yaml, que adiciona um modelo de chat falso de tests/support/.

./scripts/ensure-local-env.sh
docker compose -f compose.yaml -f compose.test.yaml up -d --build
python -m tests.e2e.test_end_to_end
python -m tests.e2e.test_formats
python -m tests.e2e.test_bind_address   # AM_BIND_ADDRESS=0.0.0.0 via non-loopback IP

make smoke executa a mesma pilha e os mesmos scripts. Os comandos do Compose precisam de um .env com AM_STORE_KEY e AM_STORE_SECRET definidos (incluindo docker compose down), porque essas variáveis são exigidas por compose.yaml.

docs/architecture.md, docs/operations.md, CONTRIBUTING.md.

Como isso foi construído

ArtifactSmith foi criado com vibe-coding. Uma pessoa definiu a direção e tomou as decisões, e agentes de IA escreveram e revisaram o código:

  • Grok Bot conduziu o projeto. Planejou o trabalho, revisou cada pull request arquivo por arquivo e decidiu o que precisava ser corrigido antes de qualquer merge.
  • Cursor agentes em nuvem escreveram o código, os testes e a documentação, e corrigiram o que as revisões encontraram.
  • Hermes agentes fizeram verificações independentes. Um revisou o código como engenheiro sênior e aprovou cada rodada. Outro instalou do zero, seguindo apenas este README, como um novo usuário faria.

Antes do lançamento, passou por:

  • Várias rodadas de revisão de código linha por linha, cada uma repetida até não restarem problemas bloqueadores.
  • Uma auditoria de segurança independente com Semgrep, Bandit, gitleaks, pip-audit e Trivy, além de um modelo de ameaças escrito.
  • Testes práticos de ataque ao limpador de HTML, ao tratamento de links e aos formatos de exportação.
  • Builds reais com um modelo ao vivo em todos os cinco formatos, verificando que os links permanecem clicáveis e que nada é carregado de fora ao abrir um documento.
  • Instalações limpas em uma máquina separada, incluindo o serviço para outras máquinas em uma rede local.
  • Verificações automatizadas a cada alteração: lint, verificação estrita de tipos, testes unitários em Python 3.11 e 3.12, build Docker, varredura de dependências e varredura de segredos.

O relatório de auditoria está em docs/security-audit-2026-10-09.md. Se você encontrar algo que deixamos passar, veja SECURITY.md.

Licença

MIT. As regras de estilo e visuais da casa são adaptadas de Humanizer (MIT) e Anthropic frontend-design (Apache-2.0). A atribuição está em NOTICE; os textos de licença de terceiros estão em licenses/. A imagem Docker inclui esses arquivos em /app.