Na série IPFS com o Nabu, o nó já guarda blocos e fala com a rede. Este post é a porta de fora: o processo que sobe sozinho e a biblioteca que outra aplicação embute.
A proposta
Quem opera um nó quer o mesmo tipo de chamada que já usa com o Kubo, a implementação de referência do IPFS em Go: HTTP (Hypertext Transfer Protocol) local, um caminho /api/v0/..., o CID (Content Identifier) na query. Quem escreve uma aplicação quer o contrário: nenhuma porta HTTP, o nó como objeto Java dentro do processo.
A proposta do Nabu é oferecer os dois. A superfície HTTP é de propósito menor que a do Kubo. O objeto embutido expõe o mesmo nó que essa superfície usa por baixo.
A Demonstração
O modo standalone lê um diretório de repositório, no estilo do Kubo. A variável de ambiente ou o argumento IPFS_PATH aponta para ele. Na ausência, o diretório é ~/.ipfs, debaixo do home do usuário. Lá estão o arquivo config e os dados. O config é JSON (JavaScript Object Notation) com a identidade, os endereços e o datastore. O processo abre a API (Application Programming Interface) HTTP no host e na porta que o config chama de Addresses.API.
Uma chamada típica é POST /api/v0/block/get?arg=<cid>. O corpo da resposta são os bytes do bloco. block/put manda os bytes num corpo multipart, o formato em que cada parte traz um arquivo, e recebe de volta um JSON com o campo Hash.
O modo embutido não abre essa porta. A aplicação chama EmbeddedIpfs.build com as peças que escolhe: store de registros, blockstore, se deve anunciar blocos, endereços de swarm, bootstrap, identidade, authoriser e, se quiser, um handler HTTP entre pares. start entra na rede. A partir daí getBlocks, publishValue e o blockstore são chamadas locais, no mesmo processo.
A API compatível com o Kubo cobre bloco, identidade, refs, um pedaço da DHT (Distributed Hash Table) e a leitura IPNS (InterPlanetary Name System). Caminhos de arquivo, add de diretório e pin não estão nessa lista.
No Nabu
A classe do processo é Nabu, em src/main/java/org/peergos/Nabu.java. getIPFSPath lê o argumento IPFS_PATH ou, sem ele, HOME mais .ipfs. readConfig abre config nesse diretório. Se o arquivo não existe, grava um config padrão. O argumento s3.datastore, quando vem na linha de comando, preenche o mount S3 antes dessa gravação. O authoriser é (c, p, a) -> CompletableFuture.completedFuture(true): toda leitura de bloco é autorizada. O store de IPNS é um DatabaseRecordStore em datastore/h2-v2.datastore. O blockstore sai de buildBlockStore. EmbeddedIpfs.build recebe provideBlocks verdadeiro, os endereços de swarm, o bootstrap e a identidade do config.
Se config.addresses.enableUPnP está ligado, enablePortForwarding roda antes de start. start(false) faz o bootstrap de forma síncrona. O endereço da API é o argumento Addresses.API, se foi passado, ou config.addresses.apiAddress. O servidor é com.sun.net.httpserver.HttpServer, com fila de 500 conexões e um pool de 50 threads. O contexto /api/v0/ recebe um APIHandler. Se o config tem Addresses.ProxyTarget, o HTTP entre pares encaminha para esse host e porta, e o servidor ganha o contexto /p2p/. Um shutdown hook para o servidor HTTP com espera de 3 segundos. Métricas, se config.metrics.enabled, sobem noutro endereço, o do MetricsSection.
APIHandler exige que o caminho comece com /api/v0/. O resto entra num switch. Os casos, em src/main/java/org/peergos/net/APIHandler.java:
-
iddevolve JSON com o PeerId em base58, no campoID. -
versiondevolve o campoVersion. A constante do handler é0.9.0. -
block/getexige umarg. Monta umWantcom o CID e oauthopcional.peersvira um conjunto de PeerIds.persistvem da query e, se falta, vale verdadeiro. A resposta são os bytes, ou status 400 se a lista volta vazia. -
block/putexigeformat. Lê um único arquivo do multipart, cujo boundary vem do headerContent-Type. Recusa tamanho acima demaxBlockSize, o teto da mensagem Bitswap, 2 MiB. O codec sai deCid.Codec.lookupIPLDName. A resposta JSON trazHash. -
block/rmapaga um CID. Sucesso devolveHasheErrorvazio. Falha responde 400. -
block/rm/bulklê uma listacidsno corpo JSON. -
block/statbusca o bloco compersistfalso e devolve sóSize. A lista de pares vai vazia. Falha responde 400. -
block/stat/bulklê{"wants":[...]}, busca compersistverdadeiro e, para cada bloco, devolvesize, os links que não são multihash do tipoidentity, e os BATs (Block Access Token, o segredo de leitura) encontrados no bloco. Responde 400 se o número de blocos não fecha com o de wants. -
block/hasresponde os bytestrueoufalse. -
refs/locallista os CIDs deblockstore.refs. O parâmetrouse-block-storevale falso se não vier. Cada CID sai num JSON com o campoRef. -
bloom/addchamablockstore.bloomAdde responde o booleano. -
dht/findprovsexige o CID.num-providerspositivo substitui o padrão 20. Cada provedor viraIDeAddrsdentro deResponses. -
ipns/getexige o argumentosigner, um PeerId em base58. ChamaresolveRecordspedindo pelo menos um resultado, ordena, e devolvesigedataem hexadecimal do registro de maior sequence. Lista vazia viraCouldn't resolve.
block/get com peers vazio deixa o BitswapBlockService tentar os conectados e depois a DHT. persist falso busca sem gravar no store local.
O cliente Java dessa API é NabuClient, em src/main/java/org/peergos/client/NabuClient.java. O construtor de host e porta assume /api/v0/. O construtor de multiaddr, por exemplo /ip4/127.0.0.1/tcp/5001, lê host e porta dali. Timeout de conexão: 10 segundos. Timeout de leitura: 60 segundos. Os exemplos do último post usam esse cliente contra um daemon.
Embutir segue o trecho do README. A identidade pode ser gerada na hora:
HostBuilder builder = new HostBuilder().generateIdentity();
PrivKey privKey = builder.getPrivateKey();
PeerId peerId = builder.getPeerId();
IdentitySection identity = new IdentitySection(privKey.bytes(), peerId);
EmbeddedIpfs.build recebe o RamRecordStore ou um store durável, o FileBlockstore, a lista de swarm e de bootstrap, e o authoriser. ipfs.start() bloqueia até o host escutar, o identify estar registrado e o bootstrap da tabela ter corrido. getBlocks devolve List<HashedBlock>. Cada item traz o CID e o byte[].
O que fica de fora: a API não replica o Kubo inteiro. Não há files, dag, name/publish HTTP, nem gateway de conteúdo no estilo ipfs.io. O campo Addresses.Gateway existe no config e é lido por AddressesSection. O handler que sobe é o da API, mais o proxy /p2p/ quando há alvo. O pacote Maven publicado pelo JitPack é com.github.peergos:nabu.
Nesta série
O anterior é Onde o bloco mora. O próximo é HTTP entre pares.
Top comments (0)