Para quem é
Uma máquina que já fica ligada — um NAS, um Raspberry, um pequeno servidor caseiro, uma caixa num rack que já paga — com um gigabyte ou mais de espaço de que não precisa, na mesma LAN que um node Sensmos. Linux com Python 3, nada para instalar além do próprio script. Root só é preciso para escrever o ficheiro de estado e a unit do systemd.
O comprador é sempre outro dono de node: o Store é comércio interno entre pessoas que têm nodes, não uma nuvem pública. Nunca lida com o comprador diretamente; o backend escolhe vendedores, move os bytes e fecha os dias.
O que guarda de facto
Blobs cifrados. A app do comprador cifra cada ficheiro no telemóvel com uma chave derivada da assinatura da carteira dele; o que lhe chega é texto cifrado com um id aleatório. Não recebe chave, nome de ficheiro nem nada que identifique o comprador. Do seu lado não existe caminho no código capaz de decifrar — o agente é um único ficheiro legível, veja por si.
Desde store-py/0.2 fica ao lado de cada blob um pequeno ficheiro .meta: a chave do ficheiro embrulhada, o nome cifrado, os tamanhos e os hashes dos blocos, assinados pela carteira do comprador. Não muda nada no que consegue ler — a chave lá dentro está selada para o comprador e não serve a mais ninguém. Existe para que os ficheiros nos sobrevivam: até agora esses dados viviam só na base de dados do backend, e perdê-la significaria perder os ficheiros apesar de os bytes estarem vivos no seu disco. O registo viaja com os bytes, por isso a segunda cópia leva um igual.
Início rápido
Execute isto na máquina que vai guardar os dados. Fala uma vez com o seu node, pela LAN, e daí em diante só com o backend.
# 1 — obter o agente (um ficheiro, só biblioteca padrão)
sudo curl -fsSL -o /usr/local/bin/sensmos-store.py https://sensmos.com/ext/sensmos-store.py
sudo chmod +x /usr/local/bin/sensmos-store.py
# 2 — emparelhar através do node: o endereço dele na LAN, o PIN, um nome, onde guardar os blobs, quanto oferece
sudo sensmos-store.py --pair 192.168.1.50 --pin 123456 --name "NAS na cave" \
--dir /srv/sensmos-store --capacity 100G
# 3 — instalar e arrancar como serviço que sobrevive a reinícios
sudo sensmos-store.py --install-service
# 4 — acompanhar
journalctl -u sensmos-store -f
O emparelhamento imprime OK — attachment …, token saved to /etc/sensmos-store.json. O registo do serviço mostra depois:
21:14:02 store agent store-py/0.3 dir=/srv/sensmos-store capacity=100 GB used=0.00 GB
21:14:03 connected as 3f9a1c2e
A partir desse momento está no pool. O primeiro comprador aparece no registo como put 7d0b… 18.1 MB … seguido de put 7d0b… done, e as provas como proof 7d0b… block 3: answered (10 MB hashed).
O endereço do node na LAN está na lista de clientes do router; o PIN é o PIN do node na app. O node tem de estar online (ligado ao backend) no momento do emparelhamento — encaminha o seu pedido e a resposta do backend.
Como é pago
| o quê | condições |
|---|---|
| preço | 0,1 GALU por GB por cópia por dia — o comprador escolhe o tamanho, a partir de 1 GB, e pode alterá-lo depois em 1 GB; 10 GB são 1 GALU por dia por cópia |
| a sua parte | 70%; 30% voltam ao pool de recompensas |
| pago por | cada dia em que o pacote de um comprador está fixo consigo e você esteve presente: com os ficheiros dele no seu disco, a sua cópia passou uma prova; enquanto o pacote ainda está vazio, o seu agente reportou nesse dia — o espaço está reservado de qualquer forma |
| não pago por | dias offline ou com prova falhada; dias em que o comprador não tinha GALU — esse dia conta como atraso para o comprador (envios pausados, cópias libertadas após 14 dias sem pagamento) e você não é pago por ele |
O acerto corre uma vez por hora no backend e regista cada (dia, comprador, vendedor) uma única vez. O GALU entra no saldo da sua carteira como qualquer outra receita de serviço.
Provas — como o backend sabe que ainda tem o ficheiro
Três vezes por dia por cópia, o backend escolhe um bloco aleatório de 10 MB de um ficheiro aleatório que guarda e envia um sal. O seu agente faz o hash do bloco com o sal e responde com 32 bytes — não se movem dados, do nosso lado não se calcula nada. Com duas cópias em dois vendedores, as duas respostas são comparadas entre si. Quando divergem, ou quando só uma cópia está online, o backend pede o bloco em bruto e confere-o com a lista de hashes que o comprador forneceu ao enviar.
Três provas falhadas seguidas e a cópia é marcada como descartada: acabam os desafios, acaba o pagamento. Não há outra penalização — simplesmente deixa de ganhar com ela.
Capacidade e como os vendedores são escolhidos
--capacityé o que oferece. Não é verificado — quem declara mais do que tem falha as provas e não ganha nada, e essa é toda a verificação.- O que conta é o espaço livre no livro do próprio backend (o que ele colocou consigo), não o que o seu agente reporta; o relatório de cinco em cinco minutos é informativo.
- O comprador escolhe o tamanho do pacote na compra — qualquer número inteiro de gigabytes, limitado pelo que os vendedores têm realmente livre — e depois aumenta-o ou reduz gigabyte a gigabyte nos mesmos vendedores. Cada gigabyte inteiro livre da sua oferta pode ser vendido.
- Os vendedores são ordenados por espaço livre; as duas cópias de um comprador vão sempre para dois donos diferentes e nunca para as máquinas do próprio comprador.
- Uma vez escolhido fica fixo: todos os ficheiros que esse comprador enviar daí em diante ficam consigo, e quando ele compra mais um gigabyte, tem de caber consigo. Também pode encolher o pacote, um gigabyte de cada vez, até ao que os ficheiros dele ocupam. O pacote dele não se move sozinho para lado nenhum. O comprador pode fechá-lo a qualquer momento: os ficheiros dele são apagados do seu disco, o espaço fica livre de novo e os pagamentos terminam nesse dia.
Para mudar a oferta, edite capacity_b em /etc/sensmos-store.json e reinicie o serviço. Para deixar de vender, pare o serviço — sem provas as suas cópias são descartadas dentro de um dia e sai do pool; os blobs em --dir são seus para apagar.
Rede
O agente liga-se para fora a sensmos.com por TLS e mantém essa única ligação. O controlo — que ficheiro guardar, que bloco provar, o que apagar — chega sempre por ela, e isso funciona atrás de CGNAT sem porta de entrada.
Os bytes dos ficheiros são outra questão. Vão diretamente entre si e o dono do ficheiro sempre que um dos dois possa ser contactado:
- é possível ligar-lhe a si — o dispositivo do dono liga-se à sua porta e o ficheiro passa diretamente;
- é possível ligar-lhe a ele — então é você que liga;
- a nenhum dos dois — os bytes passam pelo backend, como sempre passaram.
Qual das três, decide o backend, de cada vez, e verifica a alcançabilidade mesmo antes da transferência. Um endereço que funcionou há uma hora não prova nada: os telemóveis mudam de rede e os reencaminhamentos do router desaparecem depois de um reinício. O agente nunca escolhe a rota sozinho.
A porta onde escuta (9033 por omissão) responde a cada ligação com uma linha que identifica este agente. É também assim que a alcançabilidade é medida — o backend liga à porta ele próprio, porque vendedores convencidos de que têm endereço público sem o ter há demasiados para confiar numa declaração. Se o seu router falar UPnP, a porta é reencaminhada automaticamente; --no-upnp se preferir tratar disso à sua maneira, --no-direct para não escutar de todo.
13:16:55 direct: listening on TCP 9033
13:21:55 direct_ok: Reachable from the internet at 203.0.113.9:9033.
13:24:03 direct recv 8f2a1c7d with 198.51.100.4: ok (12.4 MB)
Um anfitrião a que não se consegue ligar continua a vender tal como antes — os seus bytes seguem simplesmente pela terceira rota.
O que verifica
Cada byte que recebe é somado à medida que chega e comparado com o registo que o dono assinou antes de o ficheiro sequer sair do dispositivo dele: bloco a bloco, depois o ficheiro inteiro. Um ficheiro que não corresponda nunca chega a ser escrito — comunica o bloco defeituoso e nada chega ao seu disco.
Isto protege-o a si, não a nós. Um envio corrompido passava a ser problema seu: ficava lá, falhava a prova diária e custava-lhe o pagamento do dia pelo erro de outra pessoa. Agora é recusado à porta. A verificação corre nas três rotas, incluindo cópias trazidas de outro anfitrião.
Onde vê isto
Na app (1.5.60 ou mais recente), no cartão do node que responde pelo agente, na secção Adições: online ou não, espaço oferecido e usado, cópias guardadas, provas dos últimos sete dias, ganhos. Um toque longo desliga o agente (o token é revogado de imediato). O registo do serviço mostra o mesmo, linha a linha.
Flags
| flag | significado |
|---|---|
--pair IP --pin PIN --name "…" | emparelhamento único através de um node |
--dir DIR | onde ficam os blobs (predefinição /var/lib/sensmos-store) |
--capacity 100G | espaço oferecido; sufixos M, G, T |
--install-service | copia para /usr/local/bin, escreve e ativa a unit do systemd |
--be URL | backend alternativo, para testes |
--direct-port 9033 | porta de entrada que responde com a linha de identificação do agente |
--no-direct | não escutar em nenhuma porta de entrada |
--no-upnp | não pedir ao router o reencaminhamento dessa porta (por omissão pede e renova enquanto corre) |
Estas três definições são gravadas em /etc/sensmos-store.json e o programa termina — o serviço arranca o script sem argumentos, por isso passe-as uma vez e reinicie-o.
A unit que escreve:
# /etc/systemd/system/sensmos-store.service
[Unit]
Description=Sensmos store agent
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/bin/python3 /usr/local/bin/sensmos-store.py
Restart=always
RestartSec=10
User=root
[Install]
WantedBy=multi-user.target
Emparelhamento — o token
O agente não tem credenciais próprias. Pede ao seu node (com o PIN do node, de dentro da LAN) que responda por ele; o node encaminha o pedido ao backend, e o backend emite um token com o âmbito store.serve — servir sessões Store, nada mais. O token fica em /etc/sensmos-store.json (modo 0600) e o agente renova-o sozinho. Um node pode responder por até quatro acessórios. O contrato completo está em Acessórios e a API EXT.
Atualizações
O agente nunca se atualiza sozinho: corre como root na sua LAN, e um servidor capaz de lhe enviar código seria uma porta para a sua rede. Para atualizar, repita o passo 1 e reinicie:
sudo curl -fsSL -o /usr/local/bin/sensmos-store.py https://sensmos.com/ext/sensmos-store.py
sudo systemctl restart sensmos-store
A partir de store-py/0.4 os ficheiros seguem diretamente e o que recebe é verificado. Um script mais antigo continua a vender, a provar e a ganhar, mas cada byte passa nele pelo backend e aceita um ficheiro corrompido sem dar por isso. A app mostra no cartão do vendedor quando existe uma versão mais recente.
A partir de store-py/0.5, uma transferência interrompida retoma em vez de recomeçar. Se a ligação cair a meio de um ficheiro, a parte já recebida fica no disco e a tentativa seguinte continua a partir daí — o mesmo nos dois sentidos e o mesmo na reconstrução de cópias entre anfitriões. Só oferece de volta a parte que verificou, por isso um ficheiro retomado é conferido com o mesmo rigor de um que chegou de uma só vez.
A atualização não lhe tira nada. Os ficheiros que já guarda, o formato no disco, a prova diária e os seus ganhos ficam intactos — a 0.5 lê-os e responde por eles tal como a 0.4. Um pedaço por terminar é espaço de trabalho, nunca dados guardados: desaparece no momento em que o dono cancela o envio, ou ao fim de uma semana se ninguém voltar — e em qualquer dos casos não é pago nem lhe é cobrado.
Um agente desatualizado deixa de receber ficheiros novos. A partir de store-py/0.2 o agente escreve o registo .meta descrito acima; um script que não o consiga fica de fora na escolha de vendedores — continua a servir e a provar o que já tem, mas nada de novo lhe chega. A app di-lo no cartão do vendedor, e o registo em cada ligação.
Primeiros tempos
O Store é novo e o pool é pequeno. Se montar um vendedor e durante algum tempo não chegar nada, é o estado honesto da procura, não uma falha do seu lado — diga olá no Discord e dizemos-lhe como está o pool.