Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintignore

This file was deleted.

20 changes: 0 additions & 20 deletions .eslintrc.js

This file was deleted.

54 changes: 54 additions & 0 deletions .github/workflows/deploy-image.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Build and Push Docker Image to GitHub Container Registry

on:
push:
branches:
- main
- feature/encore # Add your branch here

permissions:
contents: read
packages: write # Needed to push to GitHub Packages (ghcr.io)

jobs:
build-push-image:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} # GitHub automatically provides this token

- name: Download Encore CLI script
# Using curl directly as suggested in some setups
run: curl --output install.sh -L https://encore.dev/install.sh

- name: Install Encore CLI
run: bash install.sh

- name: Build Docker image with Encore
# Using a fixed image name 'ordfs-server'.
# Remove --config flag for now, assuming no complex infra config needed yet.
run: /home/runner/.encore/bin/encore build docker ordfs-server

- name: Tag Docker image
# Tags the image with 'latest' and a unique SHA for ghcr.io
run: |
IMAGE_ID=ghcr.io/${{ github.repository }}/ordfs-server
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[:upper:]' '[:lower:]')
echo IMAGE_ID=$IMAGE_ID
docker tag ordfs-server:latest $IMAGE_ID:latest
docker tag ordfs-server:latest $IMAGE_ID:${{ github.sha }}

- name: Push Docker image to ghcr.io
run: |
IMAGE_ID=ghcr.io/${{ github.repository }}/ordfs-server
IMAGE_ID=$(echo $IMAGE_ID | tr '[:upper:]' '[:lower:]')
docker push $IMAGE_ID:latest
docker push $IMAGE_ID:${{ github.sha }}
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
node_modules
.env
.env
dist
encore.gen.go
encore.gen.cue
/.encore
/encore.gen
2 changes: 1 addition & 1 deletion DigitalOceanApp.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Setting up ordfs-server on DigitalOcean App Platform
## Go from zero to a self-hosted Bitcoin website in just five minutes!

1. Fork the [ordfs-server repository on GitHub](https://github.com/shruggr/ordfs-server).
1. Fork the [ordfs-server repository on GitHub](https://github.com/b-open-io/ordfs-server).
2. Log into your DigitalOcean dashboard.
3. Navigate to "Apps" in the "Manage" menu.
4. Click on "Create App".
Expand Down
136 changes: 83 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,63 +1,93 @@
# OrdFS Server

This project provides an ExpressJS server to host your website from an Ordinal.
This project provides a server, built with [Encore.dev](https://encore.dev/), to serve websites and files inscribed on the Bitcoin SV blockchain. It supports resolving inscriptions via Transaction IDs (TXIDs), Outpoints (TXID_vout), and DNS-based pointers.

### Prerequisites
- NodeJS (v18+)
- NPM (comes with NodeJS)
- tsc (npm install -g typescript)
## Features

### Stand-alone
`npm install`
`npm run start`
* Serves inscription content directly from BSV.
* Resolves pointers via TXID, Outpoint (e.g., `txid_0`), and DNS TXT records.
* Built with Encore.ts for a modern, type-safe backend.
* Includes basic block information endpoints (e.g., `/v1/bsv/block/latest`).

Navigate to `http://localhost:8080/` to view the demo page, or `http://localhost:8080/971388081f6601b0e502adbfceef68d152e7f27ba5aff0230d2567aaa8acb768_0` to render an inscription.
## Prerequisites

### Integrate with existing Express server
```
import { RegisterRoutes } from 'ordfs-server';
* **Node.js** (v18+ recommended)
* **Bun** (for package management and running scripts - `npm install -g bun`)
* **Encore CLI:** Follow the installation instructions at [encore.dev/docs/install](https://encore.dev/docs/install).
* **Docker Desktop:** Required by Encore for running local development services (like databases, if used - Redis is used by this project for caching). Ensure Docker is running.

const app = express();
...
## Getting Started

RegisterRoutes(app);
```
1. **Clone the repository:**
```bash
git clone <repository-url>
cd ordfs-server
```

### DNS Registration
2 DNS records are required

1. `A` or `CNAME` record to point domain to wherever this server is running
2. `TXT` record for the same domain, which points sever to InscriptionId which should function as home page for the domain
- `ordfs=<InscriptionID>`
- hostname should be prefixed with `_ordfs.` ex `_ordfs.subdomain.example.com`

### Usage
To get block height and hash, make a GET request to `/v1/{network}/block/latest`

```html
<script type="application/javascript">
document.addEventListener("DOMContentLoaded", async () => {
const response = await fetch("/v1/bsv/block/latest");
const { height, hash } = await response.json();
console.log("Welcome to ORD-FS", { height, hash });
});
</script>
```
2. **Install dependencies:**
```bash
bun install
```

3. **Initialize Encore (if first time running an Encore app):**
You might need to run `encore auth login` if you haven't used Encore before.

4. **Run the server:**
```bash
encore run
```
This will start the Encore development server, typically at `http://localhost:4000`. You'll also get a link to the Encore local development dashboard (usually `http://localhost:9400`).

## Usage

Once the server is running:

* **Root Page:** Navigate to `http://localhost:4000/` to view the main page.
* **Fetch Inscription by TXID/Outpoint:**
* `http://localhost:4000/<txid>`
* `http://localhost:4000/<txid_vout>` (e.g., `http://localhost:4000/c7464f399365837cb6f72820f63a37f0709dd9f45f771243ebbac1d07716d72a_0`)
* Can also be a B protocol or Ordinal TXID - it will resolve to the first one.
* **Fetch Inscription by DNS Pointer:**
* `http://localhost:4000/<your.dns.pointer>` (e.g., `http://localhost:4000/satoshi.bsv`)
* **Block Information:**
* `http://localhost:4000/v1/bsv/block/latest`
* `http://localhost:4000/v1/bsv/block/height/:height`
* `http://localhost:4000/v1/bsv/block/hash/:hash`

### DNS Registration for Pointers

To use DNS pointers:

1. Ensure your domain's `A` or `CNAME` record points to the server where this Ordfs instance is running.
2. Create a `TXT` record for the domain/subdomain you wish to use. The `TXT` record should point to the Inscription ID (TXID or Outpoint) that serves as the content for that name.
* Format: `ordfs=<InscriptionID>` (where InscriptionID is `txid` or `txid_vout`)
* Hostname: Prefix the hostname with `_ordfs.`. For example, if your pointer is `mypointer.example.com`, the TXT record should be for `_ordfs.mypointer.example.com`.

### Config
#### General
`ORDFS-NAME=` Name of your OrdFS instance. This will be displayed on the demo page.

#### BTC Node
Set the following ENVIRONMENT VARS to point to your BTC Node. REST server must be enabled.
`BTC_HOST=`
`BTC_PORT=`
`BTC_UN=`
`BTC_PW=`

#### BSV Node (Optional. JungleBus is used as a fallback if not configured)
Set the following ENVIRONMENT VARS to point to your BSV Node. REST server must be enabled.
`BITCOIN_HOST=`
`BITCOIN_PORT=`
`BITCOIN_UN=`
`BITCOIN_PW=`
## Project Structure & Architecture

The server is built using Encore and is structured into services:

* **`apiService`**: Handles incoming user requests, routing, serving the main HTML page, and orchestrating calls to other services.
* **`bitcoinService`**: Responsible for direct interaction with BSV data sources, fetching inscription content, and providing block information.

For more details on the migration and architecture, see `plan.md`.

## Deployment

For deploying to DigitalOcean's App Platform, refer to the detailed guide: [Deploying to DigitalOcean App Platform](./DigitalOceanApp.md).

## Configuration

* **Redis for Caching:** The application uses Redis for caching inscription data. Encore manages the Redis instance locally when you run `encore run` (via Docker). For cloud deployments, Encore can provision Redis.


## Linting and Formatting

This project uses Biome for linting and formatting.
```bash
bun lint
bun format
# To fix issues:
bun lint:fix
bun lint:fix:unsafe
```
69 changes: 69 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"organizeImports": { "enabled": true },
"linter": {
"enabled": true,
"rules": {
"recommended": false,
"complexity": {
"noExtraBooleanCast": "error",
"noMultipleSpacesInRegularExpressionLiterals": "error",
"noUselessCatch": "error",
"noWith": "error"
},
"correctness": {
"noConstAssign": "error",
"noConstantCondition": "error",
"noEmptyCharacterClassInRegex": "error",
"noEmptyPattern": "error",
"noGlobalObjectCalls": "error",
"noInvalidBuiltinInstantiation": "error",
"noInvalidConstructorSuper": "error",
"noNonoctalDecimalEscape": "error",
"noPrecisionLoss": "error",
"noSelfAssign": "error",
"noSetterReturn": "error",
"noSwitchDeclarations": "error",
"noUndeclaredVariables": "error",
"noUnreachable": "error",
"noUnreachableSuper": "error",
"noUnsafeFinally": "error",
"noUnsafeOptionalChaining": "error",
"noUnusedLabels": "error",
"noUnusedPrivateClassMembers": "error",
"noUnusedVariables": "error",
"useIsNan": "error",
"useValidForDirection": "error",
"useYield": "error"
},
"style": { "noNonNullAssertion": "off" },
"suspicious": {
"noAsyncPromiseExecutor": "error",
"noCatchAssign": "error",
"noClassAssign": "error",
"noCompareNegZero": "error",
"noControlCharactersInRegex": "error",
"noDebugger": "error",
"noDuplicateCase": "error",
"noDuplicateClassMembers": "error",
"noDuplicateObjectKeys": "error",
"noDuplicateParameters": "error",
"noEmptyBlockStatements": "error",
"noExplicitAny": "off",
"noFallthroughSwitchClause": "off",
"noFunctionAssign": "error",
"noGlobalAssign": "error",
"noImportAssign": "error",
"noMisleadingCharacterClass": "error",
"noPrototypeBuiltins": "error",
"noRedeclare": "error",
"noShadowRestrictedNames": "error",
"noSparseArray": "error",
"noUnsafeNegation": "error",
"useGetterReturn": "error",
"useValidTypeof": "error"
}
},
"ignore": ["**/dist"]
}
}
Loading