Building the Future of Web3 Freelancing: Escrow Protection, Anti Abuse Gating, and Mobile Network Fixes on HivePostify

I have been working on HivePostify for a while, and over the last few weeks it reached a point I had been aiming for. It started as a decentralized blogging and publishing front end on the Hive blockchain. Now it is growing into a Web3 freelancing and services marketplace.

More creators and freelancers are joining, and thousands of pages are already indexed on the major search engines. That growth changed what I spend my time on. Publishing alone is no longer the main concern. The real problems now are spam, security, transparent escrow, and how the site behaves on mobile networks that are nothing like a clean office broadband line.

tp1c

This post covers the decisions behind the latest release, the problems that showed up in production, and the code that went live. I am sharing what actually changed, nothing more.

Splitting Services into Two Engines: Gigs and Custom Jobs

The first question on any freelancing platform is how work gets offered. Fiverr style products are packaged services. Upwork style products are client requests. These are two different kinds of search.

My first prototype put both into one tabbed view. It looked neat in the mockup but failed in real use. People looking for a fixed package and people looking for a custom bid were fighting over the same screen, and the filters meant different things depending on which tab was open.

So now there are two separate engines.

  1. Service Gigs ("/marketplace"): Freelancer driven. The freelancer sets fixed packages (Basic, Standard, Premium), states the delivery time, and the buyer purchases a ready offer.
  2. Job Board ("/jobs"): Client driven. The client describes a custom problem, sets a target budget in USD or AHPT, and freelancers send tailored proposals.

Each has its own route, search, and flow. Both still sit on the same wallet and the same escrow ledger, so the split is in the product and not in the money path. A buyer who wants a logo package never lands on a bid board, and a client who wants a custom build does not have to pretend it is a gig.

Anti Abuse and Spam Protection: Account Verification Middleware

Open job boards attract the usual noise: scrapers, unverified accounts posting fake offers, and low effort proposal spam. Confirmed users should not have to filter garbage before they can get to real work.

The gate sits at the API route. A project cannot be published and a proposal cannot be submitted unless the account is confirmed. The check runs against the SignupTicket ledger before the request goes any further.

// Middleware: verify confirmed standing before allowing jobs or proposals
async function checkUserPermission(username) {
  if (!username) return { allowed: false, error: 'Authentication required' };

  const ticket = await SignupTicket.findOne({
    $or: [
      { hiveUsername: username.toLowerCase().trim() },
      { freshEmailUsername: username.toLowerCase().trim() }
    ]
  });

  if (!ticket) {
    return { allowed: false, error: 'Account verification record not found.' };
  }

  // Block pending or unconfirmed trial accounts from posting or bidding
  if (ticket.status !== 'confirmed') {
    return {
      allowed: false,
      error: 'Your account is currently pending verification. Only confirmed accounts can post projects or submit proposals.'
    };
  }

  return { allowed: true, ticket };
}

On the frontend, an Axios interceptor catches the 403 and shows a plain notice that points the user to verification. I did not want a dead button that gives no explanation. I also did not want trial or pending accounts posting jobs. Confirmed standing is the minimum bar for publishing or bidding.

Escrow Architecture: Fixed Price Contracts and Dynamic Platform Fees

Why I dropped hourly billing

The first schema had an hourly option. After thinking through how disputes really happen, I removed it and kept fixed price contracts only.

Hourly work needs either a desktop time tracker, which I do not think belongs on a user's machine, or manual time logs, which usually turn into arguments. Fixed price with escrow is clearer for both sides.

  • The scope and bid amount are locked before work starts.
  • The agreed funds are reserved from the buyer's internal balance ($1 AHPT = $1.00 USD).
  • Funds move only after the buyer checks the deliverable and approves it.

Fees come from settings, not from hardcoded controllers

Earlier builds had fee percentages written directly into a few controllers. That works until a number has to change and you need a redeploy just for that. The hire path ("/api/jobs/proposals/:proposalId/hire") now reads MarketplaceSettings and supports either a flat fee or a tiered fee.

// Dynamic fee calculation from centralized MarketplaceSettings
const settings = await MarketplaceSettings.findOne();
let platformFeePercentage = settings?.platformFee?.percentage || 0;

if (settings?.platformFee?.type === 'tiered' && settings.platformFee.tiers?.length > 0) {
  const tier = settings.platformFee.tiers.find(
    t => agreedPrice >= t.minAmount && agreedPrice <= t.maxAmount
  );
  if (tier) platformFeePercentage = tier.percentage;
}

const platformFee = parseFloat(((agreedPrice * platformFeePercentage) / 100).toFixed(2));
const sellerEarnings = parseFloat((agreedPrice - platformFee).toFixed(2));

The fee rule now lives in one place, and changing a percentage does not need a new build.

Solving ERR_CONNECTION_RESET on Mobile under HTTP/3

This one took me longer than the marketplace split. Some users on mobile 4G/LTE, and some on iOS Safari, were hitting an intermittent ERR_CONNECTION_RESET. Desktop broadband was fine. The bug only appeared on certain carrier paths, which made it hard to reproduce.

Two problems were stacking on top of each other.

  1. Path MTU black holes. Many mobile networks, especially ones with CGNAT and LTE tunnels in the path, run an MTU below 1500 bytes. I often saw 1420 and 1380. Large TLS 1.3 handshakes and HTTP/3 (QUIC) datagrams were being dropped, and the routers were not sending the ICMP Fragmentation Needed message, so the client just saw a reset.
  2. Nginx header buffers. Mobile Safari sends long client hints and user agent data, and Cloudflare adds its own headers on the way in. The default client_header_buffer_size of 1k was enough to reset the socket under that load.

I did not turn off HTTP/3 or TLS 1.3 to hide the bug. The fix went into the kernel and the proxy.

Kernel settings:

# Enable Linux kernel TCP MTU probing
# Detects the real path MTU to prevent silent packet drops
sysctl -w net.ipv4.tcp_mtu_probing=1
sysctl -w net.core.somaxconn=2048

Nginx:

# Expanded client header and proxy buffers for modern mobile browsers
client_header_buffer_size 16k;
large_client_header_buffers 8 64k;
client_body_buffer_size 128k;

proxy_buffer_size 128k;
proxy_buffers 8 256k;
proxy_busy_buffers_size 256k;

keepalive_timeout 75s;
keepalive_requests 1000;

With MTU probing on and the buffers raised, those mobile sessions stopped dropping. The pages now load on the same carriers that were failing before.

Defensive UI: Stopping Layout Breakage

Job briefs are messy. People paste code, long URLs, and long unbroken strings. A normal flex or grid card will overflow sideways if you leave it unchecked.

I added defensive wrapping to the job cards and scope views.

<div
  className="text-slate-700 dark:text-slate-300 leading-relaxed text-sm"
  style={{
    whiteSpace: 'pre-wrap',
    wordBreak: 'break-word',
    overflowWrap: 'anywhere'
  }}
>
  {job.description}
</div>

overflowWrap: anywhere, together with min-w-0 on the parent, keeps the text inside the card. It is a small rule, but it stops a single long URL from wrecking the layout on a phone.

What Is Next

The freelancing flow is still in progress. Here is what I am working on next.

  • Milestone based escrow releases, so multi week jobs can pay in stages and not only at the end.
  • On chain reputation attestations, with ratings and feedback written into Hive custom JSON so trust does not live only inside my database.
  • Deliverable file exchange, with in app upload and preview so a client can sign off without leaving the job.

Decentralized software only works if the basic parts are solid: who is allowed to post, where the money sits, and whether the site loads on a normal mobile network. I would really like feedback from other Hive developers on the split between gigs and jobs, the verification gate, and the escrow fee model.

HivePostify is live at hivepostify.com.

Feedback and collaboration are welcome in the comments.



0
0
0.000
1 comments