Writing a known earth star

I recently came across a project known as earth-star and gave it a go. I am searching for p2p service that can be automated not that there are none something that can run in the browser and have some features like plus I like the name earth star.

So, I had a use case in mind , I am sure there are plenty of services out there that might be doing this but worth learning something new.

  1. I have a website that is a bit dynamic that is hosted somewhere like github or somewhere like netlify or vercel.
  2. Now if I can acheive the same level of functionality using earth star where I can deploy the website from my laptop and lower those subscription cost for applications like vercel.

So ,what i needed

  1. A service that adds the shares of the website from the client end and then syncs with the known server , in earth star unlike p2p services there is no peer concept what earth star has known servers where you can sync hence its private.
  2. So , now I have to first track all my files and then push them to the earth star , track changes and then post build add the known share that is generated to the known servers or service
  3. The service then could through another service provide the content , its now becoming complex.

To sum it up, something like IPFS where you can pin files but you must know the server before hand unlike IPFS where you can go to a service plus there is no incentive for others to host your file and etc. won't go in details. Why not use IPFS?

Now, moving ahead , I wrote a

  const url = new URL(req.url);
    if (url.pathname !== "/api/addShare") {
      return null; // Not the endpoint this extension handles
    }

    const addShare = url.searchParams.get("addShare");
    const authAddress = url.searchParams.get("authorAddress");
    const signature = url.searchParams.get("signature");
    console.log("Req for adding shares", addShare);
    if (!addShare || !authAddress || !signature) {
      console.log("Error", addShare, authAddress, signature);
      return new Response("Missing required parameters", { status: 400 });
    }

    const isValidSignature = await Earthstar.Crypto.verify(
      authAddress,
      signature,
      addShare,
    );

    if (!isValidSignature) {
      return new Response("Invalid signature", { status: 403 });
    }

    const replica = new Replica({
      driver: new ReplicaDriverFs(addShare, "./data"),
    });

    this.peer?.addReplica(replica);

    return new Response("Share added successfully", { status: 200 });

So, I am verifying the author and the signature that is essentially the share address and then adding the share.

Post this the client can sync with the service if the know the url and how to find one ask your friends or peers

peer.sync('https://overthetop.domain.com')

Now, I want some automation ,where I can push probably all of my apps or services , so a major part of the lookup which is another service that will lookup earthstar database and I am thinking of mapping the process in such a way where a subdomain assignment takes place and then where I  can simply go to these services through the subdomain.

Below a simple lookup not so dynamic , but I tried it with next js based app

// Import necessary modules from Earthstar and Oak (Deno's Express-like framework)
import {
  Peer,
  Replica,
  ReplicaDriverFs,
} from "https://deno.land/x/earthstar/mod.ts";
import { Application, Router, Context } from "https://deno.land/x/oak/mod.ts";
import * as error from "https://deno.land/x/earthstar@v10.2.2/src/util/errors.ts";

// Initialize Earthstar Replica with the file system driver
const replica = new Replica({
  driver: new ReplicaDriverFs(
    "+health.bkfn527xawsg3o2zxgcegynhrwi7ytlst6m5z6t4fszxtkfeiqvva",
    "../earth-star/data",
  ),
});

function getContentType(path: string): string {
  const ext = path.split(".").pop()?.toLowerCase();
  switch (ext) {
    case "html":
      return "text/html";
    case "css":
      return "text/css";
    case "js":
      return "application/javascript";
    case "jpg":
      return "image/jpg";
    case "jpeg":
      return "image/jpeg";
    case "png":
      return "image/png";
    case "svg":
      return "image/svg+xml";
    // Add more MIME types as needed
    default:
      return "application/octet-stream";
  }
}

const routeConfig = {
  "/": "/kindgestures/health/index.html", // Landing page
  "/contact": "/kindgestures/health/contact.html", // Contact page
  // Add more routes as needed
};

// Function to serve a document or its attachment from the Replica
async function serveDocument(context: Context, docPath: string) {
  // Extract the full requested path from the URL
  console.log(docPath);
  const requestPath = context.request.url.pathname;
  // Query the Replica for the latest document at the resolved path
  const doc = await replica.getLatestDocAtPath(docPath);
  if (!doc) {
    context.response.status = 404;
    context.response.body = "Document not found";
    return;
  }

  // Check if the document has an attachment
  if (doc.attachmentSize !== undefined) {
    const attachment = await replica.getAttachment(doc);
    if (attachment) {
      // Serve the attachment data as the response
      const body = await attachment.stream();
      context.response.body = body;
      context.response.type = getContentType(doc.path); // Dynamically set the Content-Type based on the file extension
    } else {
      // Attachment was expected but not found
      context.response.status = 404;
      context.response.body = "Attachment not found";
    }
  } else {
    // If there's no attachment, serve the document's text content
    context.response.body = doc.text;
    context.response.type = getContentType(doc.path); // Use text/plain or other appropriate type for textual content
  }
}

// Set up Oak server and routes
const app = new Application();
const router = new Router();
router.get("/", async (context) => {
  // Serve the home page document
  await serveDocument(context, "/kindgestures/health/index.html");
});

router.get("/work", async (context) => {
  // Serve the home page document
  await serveDocument(context, "/kindgestures/health/work");
});

router.get("/contact", async (context) => {
  // Serve the contact page document
  await serveDocument(context, "/kindgestures/health/contact.html");
});
router.get("/about", async (context) => {
  // Serve the contact page document
  await serveDocument(context, "/kindgestures/health/about.html");
});
// Use the serveDocument function for all GET requests

router.get("/_next/image", async (context) => {
  const urlQuery = context.request.url.searchParams.get("url");

  // Decode the URL parameter to get the image path
  const decodedUrl = decodeURIComponent(urlQuery);
  const docPath = `/kindgestures/health${decodedUrl}`;

  try {
    const doc = await replica.getLatestDocAtPath(docPath);
    if (!doc) {
      throw new Error("Image document not found");
    }

    const attachment = await replica.getAttachment(doc);
    if (!attachment) {
      throw new Error("Image attachment not found");
    }

    context.response.body = await attachment.stream();
    context.response.type = getContentType(docPath);
  } catch (error) {
    console.error(error);
    context.response.status = 404;
    context.response.body = "Image not found";
  }
});

router.get("(.*)", async (context) => {
  const filePath = context.params[0];
  await serveDocument(context, `/kindgestures/health${filePath}`); // Adjust the base path as needed
});

app.use(router.routes());
app.use(router.allowedMethods());

// Main function to orchestrate the server setup
async function main() {
  console.log("Starting Oak server...");
  await app.listen({ port: 8080 });
  console.log("Oak server running on http://localhost:8080");
}

// Run the main function
main().catch(console.error);

Now, next step is to use the service for full fledged database for certain applications since it can  store files like JSON and etc.

Ok, So , I won't be paying subscription cost to the services like Vercel, host my content at a cheap VM or probaly at home since earthstar works anywhere. Plus content will be secure and resistance to censorship. I can host storage services like 20 different locations since storage is cheap.

Github Link