diff --git a/nodejs/automation/contracting.ts b/nodejs/automation/contracting.ts
index fa9a4c4..96f9106 100644
--- a/nodejs/automation/contracting.ts
+++ b/nodejs/automation/contracting.ts
@@ -1,32 +1,28 @@
-import { Contract } from '../model/contract.ts';
-import { Ship } from '../model/ship.ts';
+import { Contract } from '../lib/types.ts';
+import { Ship } from '../lib/ships.ts';
 import * as mining from './mining.js';
 import * as selling from './selling.js';
 import * as dbContracts from '../database/contracts.ts';
-import * as dbShips from '../database/ships.ts';
-import * as api from '../lib/api.ts';
 import * as contracts from '../lib/contracts.ts';
-import * as libShips from '../lib/ships.ts';
 import * as libSystems from '../lib/systems.ts';
 import * as systems from '../lib/systems.ts';
 import * as utils from '../lib/utils.ts';
 
-export async function init(): Promise<void> {
-	const ship = dbShips.getShips()[0]; // This should always be the command ship
+export async function run(ship: Ship): Promise<void> {
 	while(true) { // we use the fact that there can only be at most one active contract at a time
 		const contracts = dbContracts.getContracts().filter(c => !c.fulfilled);
 		let contract: Contract;
 		if (contracts.length === 0) {
-			contract = await libShips.negotiate(ship);
+			contract = await ship.negotiate();
 		} else {
 			contract = contracts[0];
 		}
-		await run(contract, ship);
-		await libShips.negotiate(ship);
+		await runOne(contract, ship);
+		await ship.negotiate();
 	}
 }
 
-async function run(contract: Contract, ship: Ship): Promise<void> {
+async function runOne(contract: Contract, ship: Ship): Promise<void> {
 	await contracts.accept(contract);
 	switch(contract.type) {
 		case 'PROCUREMENT':
@@ -47,24 +43,23 @@ async function runOreProcurement(contract: Contract, ship: Ship): Promise<void>
 	const asteroids = await systems.type(ship.nav.systemSymbol, 'ENGINEERED_ASTEROID');
 	const asteroidSymbol = asteroids[0].symbol;
 	while (!contract.fulfilled) {
-		ship = dbShips.getShip(ship.symbol);
 		const goodCargo = ship.cargo.inventory.filter(i => i.symbol === wantedCargo)[0]
 		// what we do depends on where we are
 		switch (ship.nav.waypointSymbol) {
 			case asteroidSymbol:
 				await mining.mineUntilFullFor(contract, ship, asteroidSymbol);
-				await libShips.navigate(ship, deliveryPoint);
+				await ship.navigate(deliveryPoint);
 				break;
 			case deliveryPoint:
 				if (goodCargo !== undefined) { // we could be here if a client restart happens right after selling before we navigate away
 					contract = await contracts.deliver(contract, ship);
 					if (contract.fulfilled) return;
 				}
-				await libShips.navigate(ship, asteroidSymbol);
+				await ship.navigate(asteroidSymbol);
 				break;
 			default:
 				await selling.sell(ship, wantedCargo);
-				await libShips.navigate(ship, asteroidSymbol);
+				await ship.navigate(asteroidSymbol);
 		}
 	}
 }
@@ -74,7 +69,6 @@ async function runTradeProcurement(contract: Contract, ship: Ship): Promise<void
 	const deliveryPoint = deliver.destinationSymbol;
 	const wantedCargo = deliver.tradeSymbol;
 	while (!contract.fulfilled) {
-		ship = dbShips.getShip(ship.symbol);
 		const goodCargo = ship.cargo.inventory.filter(i => i.symbol === wantedCargo)[0]
 		// make sure we are not carrying useless stuff
 		await selling.sell(ship, wantedCargo);
@@ -122,14 +116,14 @@ async function runTradeProcurement(contract: Contract, ship: Ship): Promise<void
 			throw `runTradeProcurement failed, no market exports or exchanges ${wantedCargo}`;
 		}
 		// go buy what we need
-		await libShips.navigate(ship, buyingPoint);
+		await ship.navigate(buyingPoint);
 		const units = Math.min(
 			deliver.unitsRequired - deliver.unitsFulfilled,
 			ship.cargo.capacity - ship.cargo.units,
 		);
-		await libShips.buy(ship, wantedCargo, units);
+		await ship.purchase(wantedCargo, units);
 		// then make a delivery
-		await libShips.navigate(ship, deliveryPoint);
+		await ship.navigate(deliveryPoint);
 		contract = await contracts.deliver(contract, ship);
 		if (contract.fulfilled) return;
 	}
diff --git a/nodejs/automation/exploration.ts b/nodejs/automation/exploration.ts
index 67fbfe4..9ea0a5b 100644
--- a/nodejs/automation/exploration.ts
+++ b/nodejs/automation/exploration.ts
@@ -1,6 +1,6 @@
 import db from '../database/db.ts';
 import * as dbSystems from '../database/systems.ts';
-import { System } from '../model/system.ts';
+import { System } from '../lib/types.ts';
 import * as api from '../lib/api.ts';
 
 export async function init(): Promise<void> {
diff --git a/nodejs/automation/init.ts b/nodejs/automation/init.ts
index 79c5e39..1e8ea72 100644
--- a/nodejs/automation/init.ts
+++ b/nodejs/automation/init.ts
@@ -1,16 +1,16 @@
 import * as dbAgents from '../database/agents.ts';
 import * as db from '../database/db.ts';
 import * as dbContracts from '../database/contracts.ts';
-import * as dbShips from '../database/ships.ts';
 import * as dbTokens from '../database/tokens.ts';
-import { Agent } from '../model/agent.ts';
-import { Response } from '../model/api.ts';
-import { Contract } from '../model/contract.ts';
-import { Ship } from '../model/ship.ts';
-import * as api from '../lib/api.ts';
-//import * as agents from '../lib/angent.ts';
+import {
+	Response,
+} from '../lib/api.ts';
+import {
+	Agent,
+	Contract,
+} from '../lib/types.ts';
+import { Ship } from '../lib/ships.ts';
 import * as libContracts from '../lib/contracts.ts';
-import * as libShips from '../lib/ships.ts';
 
 const symbol = process.env.NODE_ENV === 'test' ? 'ADYXAX-0' : 'ADYXAX-JS';
 
@@ -32,7 +32,6 @@ export async function init(): Promise<void> {
 			case 4111:  // 4111 means the agent symbol has already been claimed so no server reset happened
 				// TODO await agents.agents();
 				await libContracts.getContracts();
-				await libShips.getShips();
 				return;
 			default:
 				throw json;
@@ -42,7 +41,4 @@ export async function init(): Promise<void> {
 	dbTokens.addToken(json.data.token);
 	dbAgents.addAgent(json.data.agent);
 	dbContracts.setContract(json.data.contract);
-	dbShips.setShip(json.data.ship);
-	// Temporary fix to fetch the data on the startup probe
-	await libShips.getShips();
 }
diff --git a/nodejs/automation/mining.ts b/nodejs/automation/mining.ts
index 272c39a..07fa19b 100644
--- a/nodejs/automation/mining.ts
+++ b/nodejs/automation/mining.ts
@@ -1,11 +1,8 @@
 import * as selling from './selling.js';
 import * as dbContracts from '../database/contracts.js';
-import * as dbShips from '../database/ships.js';
-import * as api from '../lib/api.js';
-import * as libShips from '../lib/ships.js';
-import * as utils from '../lib/utils.js';
-import { Contract } from '../model/contract.ts';
-import { Ship } from '../model/ship.ts';
+import { Ship } from '../lib/ships.js';
+import { Contract } from '../lib/types.ts';
+import { categorizeCargo } from '../lib/utils.ts';
 
 export async function mineUntilFullFor(contract: Contract, ship: Ship, asteroidSymbol: string): Promise<void> {
 	// TODO find a good asteroid
@@ -13,21 +10,20 @@ export async function mineUntilFullFor(contract: Contract, ship: Ship, asteroidS
 		await mineUntilFull(ship);
 		contract = dbContracts.getContract(contract.id);
 		const deliver = contract.terms.deliver[0];
-		ship = dbShips.getShip(ship.symbol);
-		const cargo = utils.categorizeCargo(ship.cargo, deliver.tradeSymbol);
+		const cargo = categorizeCargo(ship.cargo, deliver.tradeSymbol);
 		const wantedUnits = Object.values(cargo.wanted).reduce((acc, e) => acc += e, 0);
 		// > 90% full of the valuable goods ? Or just have enough for the contract?
 		if (wantedUnits >= ship.cargo.capacity * 0.9
 			|| cargo.wanted[deliver.tradeSymbol] >= deliver.unitsRequired - deliver.unitsFulfilled) return;
 		// we are full but need to sell junk
 		await selling.sell(ship, deliver.tradeSymbol);
-		await libShips.navigate(ship, asteroidSymbol);
+		await ship.navigate(asteroidSymbol);
 	}
 }
 
 async function mineUntilFull(ship: Ship): Promise<void> {
-	while (!libShips.isFull(ship)) {
-		await libShips.extract(ship);
+	while (!ship.isFull()) {
+		await ship.extract();
 	}
 }
 
diff --git a/nodejs/automation/selling.ts b/nodejs/automation/selling.ts
index 04b0e9d..bc586fe 100644
--- a/nodejs/automation/selling.ts
+++ b/nodejs/automation/selling.ts
@@ -1,29 +1,26 @@
 import * as dbMarkets from '../database/markets.ts';
-import * as dbShips from '../database/ships.ts';
-import * as api from '../lib/api.ts';
-import * as libShips from '../lib/ships.ts';
 import * as libSystems from '../lib/systems.ts';
-import * as utils from '../lib/utils.ts';
-import { CargoManifest } from '../model/cargo.ts';
-import { CommonThing } from '../model/common.ts';
-import { Ship } from '../model/ship.ts';
+import { categorizeCargo } from '../lib/utils.ts';
+import { Ship } from '../lib/ships.ts';
+import {
+	CargoManifest,
+	CommonThing,
+} from '../lib/types.ts';
 
 // example ctx { ship: {XXX}, keep: 'SILVER_ORE' }
 export async function sell(ship: Ship, good: string): Promise<Ship> {
     outer: while(true) {
-		ship = dbShips.getShip(ship.symbol);
 		// first lets see what we want to sell
-		let cargo = utils.categorizeCargo(ship.cargo, good);
+		let cargo = categorizeCargo(ship.cargo, good);
 		// get the marketdata from our location
 		const market = await libSystems.market(ship.nav.waypointSymbol);
 		// can we sell anything here?
 		const goods = whatCanBeTradedAt(cargo.goods, market.imports.concat(market.exchange));
 		for (let i = 0; i < goods.length; i++) {
-			await libShips.sell(ship, goods[i].symbol);
+			await ship.sell(goods[i].symbol);
 		};
 		// are we done selling everything we can?
-		ship = dbShips.getShip(ship.symbol);
-		cargo = utils.categorizeCargo(ship.cargo, good);
+		cargo = categorizeCargo(ship.cargo, good);
 		if (Object.keys(cargo.goods).length === 0) {
 			return ship;
 		}
@@ -50,7 +47,7 @@ export async function sell(ship: Ship, good: string): Promise<Ship> {
 			// if we have no data on the market we need to go there and see
 			// and if we have data and can sell there we need to go too
 			if (market === null || whatCanBeTradedAt(cargo.goods, market.imports).length > 0) {
-				await libShips.navigate(ship, waypointSymbol);
+				await ship.navigate(waypointSymbol);
 				continue outer;
 			}
 		}
@@ -60,7 +57,7 @@ export async function sell(ship: Ship, good: string): Promise<Ship> {
 			const market = await libSystems.market(waypointSymbol);
 			// if we can sell there we need to go
 			if (whatCanBeTradedAt(cargo.goods, market.exchange).length > 0) {
-				await libShips.navigate(ship, waypointSymbol);
+				await ship.navigate(waypointSymbol);
 				continue outer;
 			}
 		}
diff --git a/nodejs/database/agents.ts b/nodejs/database/agents.ts
index 3fc4182..5221dc7 100644
--- a/nodejs/database/agents.ts
+++ b/nodejs/database/agents.ts
@@ -1,4 +1,4 @@
-import { Agent } from '../model/agent.ts';
+import { Agent } from '../lib/types.ts';
 import { DbData, db } from './db.ts';
 
 const addAgentStatement = db.prepare(`INSERT INTO agents(data) VALUES (json(?));`);
diff --git a/nodejs/database/contracts.ts b/nodejs/database/contracts.ts
index 64d6c65..9adb4c8 100644
--- a/nodejs/database/contracts.ts
+++ b/nodejs/database/contracts.ts
@@ -1,4 +1,4 @@
-import { Contract } from '../model/contract.ts';
+import { Contract } from '../lib/types.ts';
 import { DbData, db } from './db.ts';
 
 const addContractStatement = db.prepare(`INSERT INTO contracts(data) VALUES (json(?));`);
diff --git a/nodejs/database/markets.ts b/nodejs/database/markets.ts
index 558d181..6e8865a 100644
--- a/nodejs/database/markets.ts
+++ b/nodejs/database/markets.ts
@@ -1,5 +1,5 @@
 import { DbData, db } from './db.ts';
-import { Market } from '../model/market.ts';
+import { Market } from '../lib/types.ts';
 import { systemFromWaypoint } from '../lib/utils.ts';
 
 const addMarketStatement = db.prepare(`INSERT INTO markets(system, data) VALUES (?, json(?));`);
diff --git a/nodejs/database/ships.ts b/nodejs/database/ships.ts
deleted file mode 100644
index c827db8..0000000
--- a/nodejs/database/ships.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { DbData, db } from './db.ts';
-import { Cargo } from '../model/cargo.ts';
-import { Fuel, Nav, Ship } from '../model/ship.ts';
-
-const addShipStatement = db.prepare(`INSERT INTO ships(data) VALUES (json(?));`);
-const getShipStatement = db.prepare(`SELECT data FROM ships WHERE data->>'symbol' = ?;`);
-const getShipsAtStatement = db.prepare(`SELECT data FROM ships WHERE data->>'$.nav.systemSymbol' = ?;`);
-const getShipsStatement = db.prepare(`SELECT data FROM ships;`);
-const setShipCargoStatement = db.prepare(`UPDATE ships SET data = (SELECT json_set(data, '$.cargo', json(:cargo)) FROM ships WHERE data->>'symbol' = :symbol) WHERE data->>'symbol' = :symbol;`);
-const setShipFuelStatement = db.prepare(`UPDATE ships SET data = (SELECT json_set(data, '$.fuel', json(:fuel)) FROM ships WHERE data->>'symbol' = :symbol) WHERE data->>'symbol' = :symbol;`);
-const setShipNavStatement = db.prepare(`UPDATE ships SET data = (SELECT json_set(data, '$.nav', json(:nav)) FROM ships WHERE data->>'symbol' = :symbol) WHERE data->>'symbol' = :symbol;`);
-const updateShipStatement = db.prepare(`UPDATE ships SET data = json(:data) WHERE data->>'symbol' = :symbol;`);
-
-export function getShip(symbol: string): Ship {
-	const data = getShipStatement.get(symbol) as DbData|undefined;
-	if (!data) throw `invalid symbol ${symbol} in getShip database call`;
-	return JSON.parse(data.data);
-}
-
-export function getShips(): Array<Ship> {
-	const data = getShipsStatement.all() as Array<DbData>;
-	return data.map(elt => JSON.parse(elt.data));
-}
-
-export function getShipsAt(symbol: string): Array<Ship> {
-	const data = getShipsAtStatement.all(symbol) as Array<DbData>;
-	return data.map(elt => JSON.parse(elt.data));
-}
-
-export function setShip(data: Ship): void {
-	const changes = updateShipStatement.run({
-		data: JSON.stringify(data),
-		symbol: data.symbol,
-	}).changes;
-	if (changes === 0) addShipStatement.run(JSON.stringify(data));
-}
-
-export function setShipCargo(symbol: string, cargo: Cargo): void {
-	setShipCargoStatement.run({
-		cargo: JSON.stringify(cargo),
-		symbol: symbol,
-	});
-}
-
-export function setShipFuel(symbol: string, fuel: Fuel): void {
-	setShipFuelStatement.run({
-		fuel: JSON.stringify(fuel),
-		symbol: symbol,
-	});
-}
-
-export function setShipNav(symbol: string, nav: Nav): void {
-	setShipNavStatement.run({
-		nav: JSON.stringify(nav),
-		symbol: symbol,
-	});
-}
diff --git a/nodejs/database/systems.ts b/nodejs/database/systems.ts
index d6d6cf1..646222e 100644
--- a/nodejs/database/systems.ts
+++ b/nodejs/database/systems.ts
@@ -1,5 +1,5 @@
 import { DbData, db } from './db.ts';
-import { System, Waypoint } from '../model/system.ts';
+import { System, Waypoint } from '../lib/types.ts';
 
 const addSystemStatement = db.prepare(`INSERT INTO systems(data) VALUES (json(?));`);
 const getSystemStatement = db.prepare(`SELECT data FROM systems WHERE data->>'symbol' = ?;`);
diff --git a/nodejs/lib/api.ts b/nodejs/lib/api.ts
index f023ccf..2bb81a6 100644
--- a/nodejs/lib/api.ts
+++ b/nodejs/lib/api.ts
@@ -3,8 +3,39 @@ import events from 'events';
 
 import { PriorityQueue } from './priority_queue.ts';
 import { getToken } from '../database/tokens.ts';
-import { APIError, Request, RequestPromise, Response } from '../model/api.ts';
-import { RateLimitError } from '../model/errors.ts';
+import { RateLimitError } from './errors.ts';
+
+export type APIError = {
+	error: string;
+	code: number;
+	data: unknown;
+};
+
+export type Meta = {
+	limit: number;
+	page: number;
+	total: number;
+}
+
+export type Request = {
+	endpoint: string; // the path part of the url to call
+	method?: string; // HTTP method for `fetch` call, defaults to 'GET'
+	page?: number; // run a paginated request starting from this page until all the following pages are fetched
+	payload?: { [key:string]: any}; // optional json object that will be sent along the request
+	priority?: number; // optional priority value, defaults to 10. lower than 10 means the message will be sent faster
+};
+
+export type RequestPromise<T> = {
+	reject: (reason?: any) => void;
+	request: Request;
+	resolve: (value: Response<T> | PromiseLike<Response<T>>) => void;
+};
+
+export type Response<T> = {
+	data: T;
+	error?: APIError;
+	meta?: Meta;
+}
 
 // queue processor module variables
 const bus = new events.EventEmitter(); // a bus to notify the queue processor to start processing messages
diff --git a/nodejs/lib/contracts.ts b/nodejs/lib/contracts.ts
index 2b6f877..833d434 100644
--- a/nodejs/lib/contracts.ts
+++ b/nodejs/lib/contracts.ts
@@ -1,20 +1,24 @@
-import { Agent } from '../model/agent.ts';
-import { APIError } from '../model/api.ts';
-import { Cargo } from '../model/cargo.ts';
-import { Contract } from '../model/contract.ts';
-import { Ship } from '../model/ship.ts';
+import {
+	Agent,
+	Cargo,
+	Contract,
+} from './types.ts';
+import {
+	APIError,
+	debugLog,
+	send,
+	sendPaginated,
+} from './api.ts';
+import { Ship } from './ships.ts';
 import * as dbAgents from '../database/agents.ts';
 import * as dbContracts from '../database/contracts.ts';
-import * as api from './api.ts';
-import * as dbShips from '../database/ships.ts';
-import * as libShips from '../lib/ships.ts';
 
 export async function accept(contract: Contract): Promise<Contract> {
 	contract = dbContracts.getContract(contract.id);
 	if (contract.accepted) return contract;
-	const response = await api.send<{agent: Agent, contract: Contract, type: ''}>({endpoint: `/my/contracts/${contract.id}/accept`, method: 'POST'});
+	const response = await send<{agent: Agent, contract: Contract, type: ''}>({endpoint: `/my/contracts/${contract.id}/accept`, method: 'POST'});
 	if (response.error) {
-		api.debugLog(response);
+		debugLog(response);
 		throw response;
 	}
 	dbAgents.setAgent(response.data.agent);
@@ -23,7 +27,7 @@ export async function accept(contract: Contract): Promise<Contract> {
 }
 
 export async function getContracts(): Promise<Array<Contract>> {
-	const response = await api.sendPaginated<Contract>({endpoint: '/my/contracts'});
+	const response = await sendPaginated<Contract>({endpoint: '/my/contracts'});
 	response.forEach(contract => dbContracts.setContract(contract));
 	return response;
 }
@@ -32,9 +36,9 @@ export async function getContract(contract: Contract): Promise<Contract> {
 	try {
 		return dbContracts.getContract(contract.id);
 	} catch {}
-	const response = await api.send<Contract>({endpoint: `/my/contracts/${contract.id}`});
+	const response = await send<Contract>({endpoint: `/my/contracts/${contract.id}`});
 	if (response.error) {
-		api.debugLog(response);
+		debugLog(response);
 		throw response;
 	}
 	dbContracts.setContract(response.data);
@@ -43,15 +47,14 @@ export async function getContract(contract: Contract): Promise<Contract> {
 
 export async function deliver(contract: Contract, ship: Ship): Promise<Contract> {
 	contract = dbContracts.getContract(contract.id);
-	ship = dbShips.getShip(ship.symbol);
 	if (contract.terms.deliver[0].unitsRequired <= contract.terms.deliver[0].unitsFulfilled) {
 		return await fulfill(contract);
 	}
 	const tradeSymbol = contract.terms.deliver[0].tradeSymbol;
 	let units = 0;
 	ship.cargo.inventory.forEach(i => {if (i.symbol === tradeSymbol) units = i.units; });
-	await libShips.dock(ship); // we need to be docked to deliver
-	const response = await api.send<{contract: Contract, cargo: Cargo}>({ endpoint: `/my/contracts/${contract.id}/deliver`, method: 'POST', payload: {
+	await ship.dock(); // we need to be docked to deliver
+	const response = await send<{contract: Contract, cargo: Cargo}>({ endpoint: `/my/contracts/${contract.id}/deliver`, method: 'POST', payload: {
 		shipSymbol: ship.symbol,
 		tradeSymbol: tradeSymbol,
 		units: units,
@@ -61,12 +64,12 @@ export async function deliver(contract: Contract, ship: Ship): Promise<Contract>
 			case 4509: // contract delivery terms have been met
 				return await fulfill(contract);
 			default: // yet unhandled error
-				api.debugLog(response);
+				debugLog(response);
 				throw response;
 		}
 	}
 	dbContracts.setContract(response.data.contract);
-	dbShips.setShipCargo(ship.symbol, response.data.cargo);
+	ship.cargo = response.data.cargo;
 	if(response.data.contract.terms.deliver[0].unitsRequired <= response.data.contract.terms.deliver[0].unitsFulfilled) {
 		return await fulfill(response.data.contract);
 	}
@@ -76,9 +79,9 @@ export async function deliver(contract: Contract, ship: Ship): Promise<Contract>
 export async function fulfill(contract: Contract): Promise<Contract> {
 	contract = dbContracts.getContract(contract.id);
 	if (contract.fulfilled) return contract;
-	const response = await api.send<{agent: Agent, contract: Contract}>({ endpoint: `/my/contracts/${contract.id}/fulfill`, method: 'POST'});
+	const response = await send<{agent: Agent, contract: Contract}>({ endpoint: `/my/contracts/${contract.id}/fulfill`, method: 'POST'});
 	if (response.error) {
-		api.debugLog(response);
+		debugLog(response);
 		throw response;
 	}
 	dbAgents.setAgent(response.data.agent);
diff --git a/nodejs/lib/errors.ts b/nodejs/lib/errors.ts
new file mode 100644
index 0000000..f9dca89
--- /dev/null
+++ b/nodejs/lib/errors.ts
@@ -0,0 +1,35 @@
+import { Cooldown } from './types.ts';
+
+export type MarketTradeVolumeError = {
+	waypointSymbol: string;
+	tradeSymbol: string;
+	units: number;
+	tradeVolume: number;
+};
+
+export type RateLimitError = {
+	type: string;
+	retryAfter: number;
+	limitBurst: number;
+	limitPerSecond: number;
+	remaining: number;
+	reset: Date;
+};
+
+export type ShipIsCurrentlyInTransitError = {
+	arrival: Date;
+	departureSymbol: string;
+	departureTime: Date;
+	destinationSymbol: string;
+	secondsToArrival: number;
+};
+
+export type ShipIsStillOnCooldownError = {
+	cooldown: Cooldown;
+};
+
+export type ShipRequiresMoreFuelForNavigationError = {
+	fuelAvailable: number;
+	fuelRequired: number;
+	shipSymbol: string;
+};
diff --git a/nodejs/lib/ships.ts b/nodejs/lib/ships.ts
index 83b2e86..7221596 100644
--- a/nodejs/lib/ships.ts
+++ b/nodejs/lib/ships.ts
@@ -1,240 +1,207 @@
-import { Response } from '../model/api.ts';
-import { Agent } from '../model/agent.ts';
-import { Cargo } from '../model/cargo.ts';
-import { Contract } from '../model/contract.ts';
-import { Cooldown, Fuel, Nav, Ship } from '../model/ship.ts';
-import * as api from './api.ts';
+import {
+	Response,
+	debugLog,
+	send,
+	sleep,
+} from './api.ts';
+import {
+	MarketTradeVolumeError,
+	ShipIsCurrentlyInTransitError,
+	ShipIsStillOnCooldownError,
+	ShipRequiresMoreFuelForNavigationError,
+} from './errors.ts';
+import {
+	Agent,
+	Cargo,
+	Contract,
+	Cooldown,
+	Fuel,
+	Nav,
+	Registration,
+} from './types.ts';
 import * as dbAgents from '../database/agents.ts';
-import * as dbShips from '../database/ships.ts';
-//import * as dbSurveys from '../database/surveys.ts';
-import * as systems from '../lib/systems.ts';
-
-export async function buy(ship: Ship, tradeSymbol: string, units: number): Promise<void> {
-	if (units <= 0) return;
-	ship = await getShip(ship);
-	await dock(ship);
-	// TODO take into account the tradevolume, we might need to buy in multiple steps
-	const response = await api.send<{agent: Agent, cargo: Cargo}>({endpoint: `/my/ships/${ship.symbol}/purchase`, method: 'POST', payload: { symbol: tradeSymbol, units: units }}); // TODO transaction field
-	if (response.error) {
-		api.debugLog(response);
-		throw response;
-	}
-	dbShips.setShipCargo(ship.symbol, response.data.cargo);
-	dbAgents.setAgent(response.data.agent);
-}
-
-export async function dock(ship: Ship): Promise<void> {
-	ship = await getShip(ship);
-	if (ship.nav.status === 'DOCKED') return;
-	const response = await api.send<{nav: Nav}>({endpoint: `/my/ships/${ship.symbol}/dock`, method: 'POST'});
-	if (response.error) {
-		switch(response.error.code) {
-			case 4214: // ship is in transit
-				const errorData = response.error.data as { secondsToArrival: number};
-				await api.sleep(errorData.secondsToArrival * 1000);
-				return await dock(ship);
-			default: // yet unhandled error
-				api.debugLog(response);
-				throw response;
-		}
-	}
-	dbShips.setShipNav(ship.symbol, response.data.nav);
-}
-
-export async function extract(ship: Ship): Promise<Cargo> {
-	ship = await getShip(ship);
-	if (await isFull(ship)) return ship.cargo;
-	// TODO move to a suitable asteroid?
-	// const asteroidFields = await systems.type({symbol: ship.nav.systemSymbol, type: 'ENGINEERED_ASTEROID'});
-	// TODO if there are multiple fields, find the closest one?
-	//await navigate({symbol: ctx.symbol, waypoint: asteroidFields[0].symbol});
-	await orbit(ship);
-	// TODO handle surveying?
-	const response = await api.send<{cooldown: Cooldown, cargo: Cargo}>({endpoint: `/my/ships/${ship.symbol}/extract`, method: 'POST'}); // TODO extraction and events api response fields cf https://spacetraders.stoplight.io/docs/spacetraders/b3931d097608d-extract-resources
-	if (response.error) {
-		switch(response.error.code) {
-			case 4000: // ship is on cooldown
-				const errorData = response.error.data as {cooldown: Cooldown};
-				await api.sleep(errorData.cooldown.remainingSeconds  * 1000);
-				return await extract(ship);
-			case 4228: // ship is full
-				return ship.cargo;
-			default: // yet unhandled error
-				api.debugLog(response);
-				throw response;
-		}
-	} else {
-		dbShips.setShipCargo(ship.symbol, response.data.cargo);
-		await api.sleep(response.data.cooldown.remainingSeconds*1000);
-	}
-	return response.data.cargo
-}
-
-export async function isFull(ship: Ship): Promise<boolean> {
-	ship = await getShip(ship);
-	return ship.cargo.units >= ship.cargo.capacity * 0.9;
-}
-
-//function hasMount(shipSymbol, mountSymbol) {
-//	const ship = dbShips.getShip(shipSymbol);
-//	return ship.mounts.filter(s => s.symbol === mountSymbol).length > 0;
-//}
-
-//export async function jump(ship: Ship): Ship {
-//	// TODO
-//	const response = await api.send({endpoint: `/my/ships/${ctx.ship}/jump`, method: 'POST', payload: { systemSymbol: ctx.system }});
-//	await api.sleep(response.data.cooldown.remainingSeconds*1000);
-//	return response;
-//}
-
-export async function navigate(ship: Ship, waypoint: string): Promise<void> {
-	ship = await getShip(ship);
-	if (ship.nav.waypointSymbol === waypoint) return;
-	await orbit(ship);
-	// TODO if we do not have enough fuel, make a stop to refuel along the way or drift to the destination
-	const response = await api.send<{fuel: Fuel, nav: Nav}>({endpoint: `/my/ships/${ship.symbol}/navigate`, method: 'POST', payload: { waypointSymbol: waypoint }}); // TODO events field
-	if (response.error) {
-		switch(response.error.code) {
-			case 4214: // ship is in transit
-				const errorData = response.error.data as { secondsToArrival: number};
-				await api.sleep(errorData.secondsToArrival * 1000);
-				return await navigate(ship, waypoint);
-			default: // yet unhandled error
-				api.debugLog(response);
-				throw response;
-		}
-	}
-	dbShips.setShipFuel(ship.symbol, response.data.fuel);
-	dbShips.setShipNav(ship.symbol, response.data.nav);
-	const delay = new Date(response.data.nav.route.arrival).getTime()  - new Date().getTime() ;
-	await api.sleep(delay);
-	response.data.nav.status = 'IN_ORBIT'; // we arrive in orbit
-	dbShips.setShipNav(ship.symbol, response.data.nav);
-	// TODO only refuel at the start of a journey, if we do not have enough OR if the destination does not sell fuel?
-	await refuel(ship);
-}
-
-export async function negotiate(ship: Ship): Promise<Contract> {
-	ship = await getShip(ship);
-	const response = await api.send<{contract: Contract}>({endpoint: `/my/ships/${ship.symbol}/negotiate/contract`, method: 'POST'});
-	if (response.error) {
-		switch(response.error.code) {
-			case 4214: // ship is in transit
-				const errorData = response.error.data as { secondsToArrival: number};
-				await api.sleep(errorData.secondsToArrival * 1000);
-				return await negotiate(ship);
-			default: // yet unhandled error
-				api.debugLog(response);
-				throw response;
-		}
-	}
-	return response.data.contract;
-}
-
-export async function orbit(ship: Ship): Promise<void> {
-	ship = await getShip(ship);
-	if (ship.nav.status === 'IN_ORBIT') return;
-	const response = await api.send<{nav: Nav}>({endpoint: `/my/ships/${ship.symbol}/orbit`, method: 'POST'});
-	if (response.error) {
-		switch(response.error.code) {
-			case 4214: // ship is in transit
-				const errorData = response.error.data as { secondsToArrival: number};
-				await api.sleep(errorData.secondsToArrival * 1000);
-				return await orbit(ship);
-			default: // yet unhandled error
-				api.debugLog(response);
-				throw response;
-		}
-	}
-	dbShips.setShipNav(ship.symbol, response.data.nav);
-}
-
-//export async function purchase(ctx) {
-//	const response = await api.send({endpoint: '/my/ships', method: 'POST', payload: {
-//		shipType: ctx.shipType,
-//		waypointSymbol: ctx.waypoint,
-//	}});
-//	if (response.error !== undefined) {
-//		throw response;
-//	}
-//	dbShips.setShip(response.data.ship);
-//	return response.data;
-//}
-
-export async function refuel(ship: Ship): Promise<void> {
-	ship = await getShip(ship);
-	if (ship.fuel.current >= ship.fuel.capacity * 0.9) return;
-	// TODO check if our current waypoint has a marketplace (and sells fuel)?
-	await dock(ship);
-	const response = await api.send<{agent: Agent, fuel: Fuel}>({endpoint: `/my/ships/${ship.symbol}/refuel`, method: 'POST'}); // TODO transaction field
-	if (response.error) {
-		api.debugLog(response);
-		throw response;
-	}
-	dbShips.setShipFuel(ship.symbol, response.data.fuel);
-	dbAgents.setAgent(response.data.agent);
-}
-
-export async function sell(ship: Ship, tradeSymbol: string): Promise<Cargo> {
-	ship = await getShip(ship);
-	// TODO check if our current waypoint has a marketplace and buys tradeSymbol?
-	await dock(ship);
-	let units = 0;
-	ship.cargo.inventory.forEach(i => {if (i.symbol === tradeSymbol) units = i.units; });
-	const response = await api.send<{agent: Agent, cargo: Cargo}>({endpoint: `/my/ships/${ship.symbol}/sell`, method: 'POST', payload: { symbol: tradeSymbol, units: units }}); // TODO transaction field
-	if (response.error) {
-		api.debugLog(response);
-		throw response;
-	}
-	dbShips.setShipCargo(ship.symbol, response.data.cargo);
-	dbAgents.setAgent(response.data.agent);
-	return response.data.cargo;
-}
 
 export async function getShips(): Promise<Array<Ship>> {
-	const response = await api.send<Array<Ship>>({endpoint: `/my/ships`, page: 1});
+	const response = await send<Array<Ship>>({endpoint: `/my/ships`, page: 1});
 	if (response.error) {
-		api.debugLog(response);
+		debugLog(response);
 		throw response;
 	}
-	response.data.forEach(ship => dbShips.setShip(ship));
-	return response.data;
+	return response.data.map(ship => new Ship(ship));
 }
 
-export async function getShip(ship: Ship): Promise<Ship> {
-	try {
-		return dbShips.getShip(ship.symbol);
-	} catch {}
-	const response = await api.send<Ship>({endpoint: `/my/ships/${ship.symbol}`});
-	if (response.error) {
-		api.debugLog(response);
-		throw response;
+export class Ship {
+	cargo: Cargo;
+	cooldown: Cooldown;
+	// crew
+	// engine
+	// frame
+	fuel: Fuel;
+	// modules
+	// mounts
+	nav: Nav;
+	// reactor
+	registration: Registration;
+	symbol: string;
+	constructor(ship: Ship) {
+		this.cargo = ship.cargo;
+		this.cooldown = ship.cooldown;
+		this.fuel = ship.fuel;
+		this.nav = ship.nav;
+		this.registration = ship.registration;
+		this.symbol = ship.symbol;
+	}
+	async dock(): Promise<void> {
+		if (this.nav.status === 'DOCKED') return;
+		const response = await send<{nav: Nav}>({endpoint: `/my/ships/${this.symbol}/dock`, method: 'POST'});
+		if (response.error) {
+			switch(response.error.code) {
+				case 4214:
+					const sicite = response.error.data as ShipIsCurrentlyInTransitError;
+					await sleep(sicite.secondsToArrival * 1000);
+					return await this.dock();
+				default: // yet unhandled error
+					debugLog(response);
+					throw response;
+			}
+		}
+		this.nav = response.data.nav;
+	}
+	async extract(): Promise<Cargo> {
+		if (this.isFull()) return this.cargo;
+		// TODO move to a suitable asteroid?
+		// const asteroidFields = await systems.type({symbol: this.nav.systemSymbol, type: 'ENGINEERED_ASTEROID'});
+		// TODO if there are multiple fields, find the closest one?
+		//await navigate({symbol: ctx.symbol, waypoint: asteroidFields[0].symbol});
+		await this.orbit();
+		// TODO handle surveying?
+		const response = await send<{cooldown: Cooldown, cargo: Cargo}>({endpoint: `/my/ships/${this.symbol}/extract`, method: 'POST'}); // TODO extraction and events api response fields cf https://spacetraders.stoplight.io/docs/spacetraders/b3931d097608d-extract-resources
+		if (response.error) {
+			switch(response.error.code) {
+				case 4000:
+					const sisoce = response.error.data as ShipIsStillOnCooldownError;
+					await sleep(sisoce.cooldown.remainingSeconds  * 1000);
+					return await this.extract();
+				case 4228: // ship is full
+					return this.cargo;
+				default: // yet unhandled error
+					debugLog(response);
+					throw response;
+			}
+		}
+		this.cargo = response.data.cargo;
+		await sleep(response.data.cooldown.remainingSeconds*1000);
+		return this.cargo;
+	}
+	isFull(): boolean {
+		return this.cargo.units >= this.cargo.capacity * 0.9;
+	}
+	async navigate(waypointSymbol: string): Promise<void> {
+		if (this.nav.waypointSymbol === waypointSymbol) return;
+		const d =
+		// TODO compute fuel consumption and refuel if we do not have enough OR if the destination does not sell fuel?
+		await this.refuel();
+		await this.orbit();
+		// TODO if we do not have enough fuel, make a stop to refuel along the way or drift to the destination
+		const response = await send<{fuel: Fuel, nav: Nav}>({endpoint: `/my/ships/${this.symbol}/navigate`, method: 'POST', payload: { waypointSymbol: waypointSymbol }}); // TODO events field
+		if (response.error) {
+			switch(response.error.code) {
+				case 4203: // not enough fuel
+					const srmffne = response.error.data as ShipRequiresMoreFuelForNavigationError;
+					// TODO test if it exceeds our maximum
+					// find an intermediate stop to refuel if that is the case
+					debugLog(response);
+					throw response;
+					//await refuel(ship);
+					//return await navigate(ship, waypoint);
+				case 4214:
+					const sicite = response.error.data as ShipIsCurrentlyInTransitError;
+					await sleep(sicite.secondsToArrival * 1000);
+					return await this.navigate(waypointSymbol);
+				default: // yet unhandled error
+					debugLog(response);
+					throw response;
+			}
+		}
+		this.fuel = response.data.fuel;
+		this.nav = response.data.nav;
+		const delay = new Date(this.nav.route.arrival).getTime()  - new Date().getTime() ;
+		await sleep(delay);
+		this.nav.status = 'IN_ORBIT'; // we arrive in orbit
+	}
+	async negotiate(): Promise<Contract> {
+		const response = await send<{contract: Contract}>({endpoint: `/my/ships/${this.symbol}/negotiate/contract`, method: 'POST'});
+		if (response.error) {
+			switch(response.error.code) {
+				case 4214:
+					const sicite = response.error.data as ShipIsCurrentlyInTransitError;
+					await sleep(sicite.secondsToArrival * 1000);
+					return await this.negotiate();
+				default: // yet unhandled error
+					debugLog(response);
+					throw response;
+			}
+		}
+		return response.data.contract;
+	}
+	async orbit(): Promise<void> {
+		if (this.nav.status === 'IN_ORBIT') return;
+		const response = await send<{nav: Nav}>({endpoint: `/my/ships/${this.symbol}/orbit`, method: 'POST'});
+		if (response.error) {
+			switch(response.error.code) {
+				case 4214:
+					const sicite = response.error.data as ShipIsCurrentlyInTransitError;
+					await sleep(sicite.secondsToArrival * 1000);
+					return await this.orbit();
+				default: // yet unhandled error
+					debugLog(response);
+					throw response;
+			}
+		}
+		this.nav = response.data.nav;
+	}
+	async purchase(tradeSymbol: string, units: number): Promise<void> {
+		if (units <= 0) return;
+		await this.dock();
+		// TODO take into account the tradevolume, we might need to buy in multiple steps
+		const response = await send<{agent: Agent, cargo: Cargo}>({endpoint: `/my/ships/${this.symbol}/purchase`, method: 'POST', payload: { symbol: tradeSymbol, units: units }}); // TODO transaction field
+		if (response.error) {
+			switch(response.error.code) {
+				case 4604: // units per transaction limit exceeded
+					const mtve = response.error.data as MarketTradeVolumeError;
+					await this.purchase(tradeSymbol, mtve.tradeVolume);
+					return await this.purchase(tradeSymbol, units - mtve.tradeVolume);
+				default:
+					debugLog(response);
+					throw response;
+			}
+		}
+		this.cargo = response.data.cargo;
+		dbAgents.setAgent(response.data.agent);
+	}
+	async refuel(): Promise<void> {
+		// TODO check if our current waypoint has a marketplace (and sells fuel)?
+		await this.dock();
+		const response = await send<{agent: Agent, fuel: Fuel}>({endpoint: `/my/ships/${this.symbol}/refuel`, method: 'POST'}); // TODO transaction field
+		if (response.error) {
+			debugLog(response);
+			throw response;
+		}
+		this.fuel = response.data.fuel;
+		dbAgents.setAgent(response.data.agent);
+	}
+	async sell(tradeSymbol: string): Promise<Cargo> {
+		// TODO check if our current waypoint has a marketplace and buys tradeSymbol?
+		await this.dock();
+		let units = 0;
+		this.cargo.inventory.forEach(i => {if (i.symbol === tradeSymbol) units = i.units; });
+		const response = await send<{agent: Agent, cargo: Cargo}>({endpoint: `/my/ships/${this.symbol}/sell`, method: 'POST', payload: { symbol: tradeSymbol, units: units }}); // TODO transaction field
+		if (response.error) {
+			debugLog(response);
+			throw response;
+		}
+		this.cargo = response.data.cargo;
+		dbAgents.setAgent(response.data.agent);
+		return this.cargo;
 	}
-	dbShips.setShip(response.data);
-	return response.data;
 }
-
-//export async function survey(ctx) {
-//	if (!hasMount(ctx.symbol, 'MOUNT_SURVEYOR_I')) { // we check if a surveyor is mounted on the ship
-//		return null;
-//	}
-//	const ship = dbShips.getShip(ctx.symbol);
-//	const asteroidFields = await systems.type({symbol: ship.nav.systemSymbol, type: 'ASTEROID_FIELD'});
-//	// TODO if there are multiple fields, find the closest one?
-//	await navigate({symbol: ctx.symbol, waypoint: asteroidFields[0].symbol});
-//	await orbit(ctx);
-//	const response = await api.send({endpoint: `/my/ships/${ctx.symbol}/survey`, method: 'POST'});
-//	api.debugLog(response);
-//	if (response.error !== undefined) {
-//		switch(response.error.code) {
-//			case 4000: // ship is on cooldown
-//				await api.sleep(response.error.data.cooldown.remainingSeconds  * 1000);
-//				return await survey(ctx);
-//			default: // yet unhandled error
-//				throw response;
-//		}
-//	}
-//	dbSurveys.set(response.data.surveys[0]);
-//	await api.sleep(response.data.cooldown.remainingSeconds*1000);
-//	return response;
-//}
diff --git a/nodejs/lib/systems.ts b/nodejs/lib/systems.ts
index dc938bb..97aa6e3 100644
--- a/nodejs/lib/systems.ts
+++ b/nodejs/lib/systems.ts
@@ -1,18 +1,24 @@
-import * as api from './api.ts';
+import {
+	debugLog,
+	send,
+	sendPaginated,
+} from './api.ts';
 import * as dbMarkets from '../database/markets.ts';
-import * as dbShips from '../database/ships.ts';
 import * as dbSystems from '../database/systems.ts';
-import { Market } from '../model/market.ts'
-import { System, Waypoint } from '../model/system.ts'
-import * as utils from './utils.ts';
+import {
+	Market,
+	System,
+	Waypoint
+} from './types.ts'
+import { systemFromWaypoint } from './utils.ts';
 
 export async function market(waypointSymbol: string): Promise<Market> {
     const data = dbMarkets.getMarketAtWaypoint(waypointSymbol);
 	if (data) { return data; }
-	const systemSymbol = utils.systemFromWaypoint(waypointSymbol);
-	let response = await api.send<Market>({endpoint: `/systems/${systemSymbol}/waypoints/${waypointSymbol}/market`});
+	const systemSymbol = systemFromWaypoint(waypointSymbol);
+	let response = await send<Market>({endpoint: `/systems/${systemSymbol}/waypoints/${waypointSymbol}/market`});
 	if (response.error) {
-		api.debugLog(response);
+		debugLog(response);
 		throw response;
 	}
 	dbMarkets.setMarket(response.data);
@@ -21,16 +27,16 @@ export async function market(waypointSymbol: string): Promise<Market> {
 
 //export async function shipyard(waypoint: string): Promise<unknown> {
 //	// TODO database caching
-//	const systemSymbol = utils.systemFromWaypoint(waypoint);
-//	return await api.send({endpoint: `/systems/${systemSymbol}/waypoints/${waypoint}/shipyard`});
+//	const systemSymbol = systemFromWaypoint(waypoint);
+//	return await send({endpoint: `/systems/${systemSymbol}/waypoints/${waypoint}/shipyard`});
 //}
 
 export async function system(symbol: string): Promise<System> {
 	let data = dbSystems.getSystem(symbol);
 	if (data) { return data; }
-	const response = await api.send<System>({endpoint: `/systems/${symbol}`});
+	const response = await send<System>({endpoint: `/systems/${symbol}`});
 	if (response.error) {
-		api.debugLog(response);
+		debugLog(response);
 		throw response;
 	}
 	dbSystems.setSystem(response.data);
@@ -54,7 +60,7 @@ export async function waypoints(systemSymbol: string): Promise<Array<Waypoint>>
 	const updated = dbSystems.getSystemUpdated(systemSymbol);
 	// TODO handle uncharted systems
 	if (updated) return s.waypoints;
-	const waypoints = await api.sendPaginated<Waypoint>({endpoint: `/systems/${systemSymbol}/waypoints`});
+	const waypoints = await sendPaginated<Waypoint>({endpoint: `/systems/${systemSymbol}/waypoints`});
 	dbSystems.setSystemWaypoints(systemSymbol, waypoints);
 	return waypoints;
 }
diff --git a/nodejs/lib/types.ts b/nodejs/lib/types.ts
new file mode 100644
index 0000000..a8e748c
--- /dev/null
+++ b/nodejs/lib/types.ts
@@ -0,0 +1,145 @@
+export type Agent = {
+	accountId: string;
+	credits: number;
+	headquarters: string;
+	shipCount: number;
+	startingFaction: string;
+	symbol: string;
+};
+
+export type CommonThing = {
+	description: string;
+	name: string;
+	symbol: string;
+};
+
+export type Cargo = {
+	capacity: number;
+	units: number;
+	inventory: Array<Inventory>;
+};
+
+// Custom type, not from space traders api
+export type CargoManifest = {
+	[key: string]: number;
+};
+
+export type Chart = {
+	waypointSymbol: string;
+	submittedBy: string;
+	submittedOn: Date;
+};
+
+export type Contract = {
+	id: string;
+	factionSymbol: string;
+	type: string;
+	terms: {
+		deadline: Date;
+		payment: {
+			onAccepted: number;
+			onFulfilled: number;
+		},
+		deliver: Array<{
+			tradeSymbol: string;
+			destinationSymbol: string;
+			unitsRequired: number;
+			unitsFulfilled: number;
+		}>;
+	};
+	accepted: boolean;
+	fulfilled: boolean;
+	expiration: Date;
+	deadlineToAccept: Date;
+};
+
+export type Cooldown = {
+	shipSymbol: string;
+	totalSeconds: number;
+	remainingSeconds: number;
+};
+
+export type Consummed = {
+	amount: number;
+	timestamp: Date;
+};
+
+export type Fuel = {
+	capacity: number;
+	consummed: Consummed;
+	current: number;
+};
+
+export type Inventory = CommonThing & {
+	units: number;
+};
+
+export type Market = {
+	symbol: string;
+	exchange: Array<CommonThing>;
+	exports: Array<CommonThing>;
+	imports: Array<CommonThing>;
+	//transactions: Array<Transaction>;
+	tradeGoods: Array<TradeGood>;
+};
+
+export type Nav = {
+	flightMode: string;
+	route: Route;
+	status: string;
+	systemSymbol: string;
+	waypointSymbol: string;
+};
+
+export type Registration = {
+	factionSymbol: string;
+	name: string;
+	role: string;
+};
+
+export type Route = {
+	arrival: Date;
+	departureTime: Date;
+	destination: RouteEndpoint;
+	origin: RouteEndpoint;
+};
+
+export type RouteEndpoint = {
+	type: string;
+	symbol: string;
+	systemSymbol: string;
+	x: number;
+	y: number;
+};
+
+export type System = {
+	symbol: string;
+	sectorSymbol: string;
+	type: string;
+	x: number;
+	y: number;
+	waypoints: Array<Waypoint>;
+};
+
+export type TradeGood = CommonThing & {
+	activity: string;
+	purchasePrice: number;
+	sellPrice: number;
+	supply: string;
+	tradeVolume: number;
+	type: string;
+};
+
+export type Waypoint = {
+	chart: Chart;
+	factions: Array<{symbol: string;}>;
+	isUnderConstruction: boolean;
+	modifiers: Array<CommonThing>;
+	orbitals: Array<{symbol: string;}>;
+	orbits: string;
+	symbol: string;
+	traits: Array<CommonThing>;
+	type: string;
+	x: number;
+	y: number;
+};
diff --git a/nodejs/lib/utils.ts b/nodejs/lib/utils.ts
index c5093a6..39378e9 100644
--- a/nodejs/lib/utils.ts
+++ b/nodejs/lib/utils.ts
@@ -1,4 +1,4 @@
-import { Cargo, CargoManifest } from '../model/cargo.ts';
+import { Cargo, CargoManifest } from './types.ts';
 
 export type CategorizedCargo = {
 	wanted: CargoManifest;
diff --git a/nodejs/main.ts b/nodejs/main.ts
index dfb7d89..2d15c60 100755
--- a/nodejs/main.ts
+++ b/nodejs/main.ts
@@ -1,10 +1,10 @@
 import * as autoContracting from './automation/contracting.ts';
 //import * as autoExploring from './automation/exploration.ts';
 import * as autoInit from './automation/init.ts';
-//import * as api from './lib/api.ts';
-//import * as contracts from './lib/contracts.ts';
+import { getShips } from './lib/ships.ts';
 
 await autoInit.init();
+const ships = await getShips();
 
-await autoContracting.init();
+await autoContracting.run(ships[0]); // dedicate the command ship to running contracts
 //autoExploring.init();
diff --git a/nodejs/model/agent.ts b/nodejs/model/agent.ts
deleted file mode 100644
index dce6424..0000000
--- a/nodejs/model/agent.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export type Agent = {
-	accountId: string;
-	credits: number;
-	headquarters: string;
-	shipCount: number;
-	startingFaction: string;
-	symbol: string;
-};
diff --git a/nodejs/model/api.ts b/nodejs/model/api.ts
deleted file mode 100644
index 69c7ee1..0000000
--- a/nodejs/model/api.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-export type APIError = {
-	error: string;
-	code: number;
-	data: unknown;
-};
-
-export type Meta = {
-	limit: number;
-	page: number;
-	total: number;
-}
-
-export type Request = {
-	endpoint: string; // the path part of the url to call
-	method?: string; // HTTP method for `fetch` call, defaults to 'GET'
-	page?: number; // run a paginated request starting from this page until all the following pages are fetched
-	payload?: { [key:string]: any}; // optional json object that will be sent along the request
-	priority?: number; // optional priority value, defaults to 10. lower than 10 means the message will be sent faster
-};
-
-export type RequestPromise<T> = {
-	reject: (reason?: any) => void;
-	request: Request;
-	resolve: (value: Response<T> | PromiseLike<Response<T>>) => void;
-};
-
-export type Response<T> = {
-	data: T;
-	error?: APIError;
-	meta?: Meta;
-}
diff --git a/nodejs/model/cargo.ts b/nodejs/model/cargo.ts
deleted file mode 100644
index 869fa49..0000000
--- a/nodejs/model/cargo.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-export type Inventory = {
-	description: string;
-	name: string;
-	symbol: string;
-	units: number;
-};
-
-export type Cargo = {
-	"capacity": number;
-	"units": number;
-	"inventory": Array<Inventory>;
-};
-
-// Custom type, not from space traders api
-export type CargoManifest = {
-	[key: string]: number;
-};
diff --git a/nodejs/model/common.ts b/nodejs/model/common.ts
deleted file mode 100644
index 1712ad8..0000000
--- a/nodejs/model/common.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export type CommonThing = {
-	description: string;
-	name: string;
-	symbol: string;
-};
diff --git a/nodejs/model/contract.ts b/nodejs/model/contract.ts
deleted file mode 100644
index eb7add4..0000000
--- a/nodejs/model/contract.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-export type Contract = {
-	id: string;
-	factionSymbol: string;
-	type: string;
-	terms: {
-		deadline: Date;
-		payment: {
-			onAccepted: number;
-			onFulfilled: number;
-		},
-		deliver: Array<{
-			tradeSymbol: string;
-			destinationSymbol: string;
-			unitsRequired: number;
-			unitsFulfilled: number;
-		}>;
-	};
-	accepted: boolean;
-	fulfilled: boolean;
-	expiration: Date;
-	deadlineToAccept: Date;
-};
diff --git a/nodejs/model/errors.ts b/nodejs/model/errors.ts
deleted file mode 100644
index 6002ae6..0000000
--- a/nodejs/model/errors.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export type RateLimitError = {
-	type: string;
-	retryAfter: number;
-	limitBurst: number;
-	limitPerSecond: number;
-	remaining: number;
-	reset: Date;
-};
diff --git a/nodejs/model/market.ts b/nodejs/model/market.ts
deleted file mode 100644
index 1ba70c1..0000000
--- a/nodejs/model/market.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { CommonThing } from 'common.ts';
-
-export type TradeGood = CommonThing & {
-	activity: string;
-	purchasePrice: number;
-	sellPrice: number;
-	supply: string;
-	tradeVolume: number;
-	type: string;
-};
-
-export type Market = {
-	symbol: string;
-	exchange: Array<CommonThing>;
-	exports: Array<CommonThing>;
-	imports: Array<CommonThing>;
-	//transactions: Array<Transaction>;
-	tradeGoods: Array<TradeGood>;
-};
diff --git a/nodejs/model/ship.ts b/nodejs/model/ship.ts
deleted file mode 100644
index bf40767..0000000
--- a/nodejs/model/ship.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { Cargo } from './cargo.ts';
-
-export type Cooldown = {
-	shipSymbol: string;
-	totalSeconds: number;
-	remainingSeconds: number;
-};
-
-export type Consummed = {
-	amount: number;
-	timestamp: Date;
-};
-
-export type Fuel = {
-	capacity: number;
-	consummed: Consummed;
-	current: number;
-};
-
-export type Nav = {
-	flightMode: string;
-	route: Route;
-	status: string;
-	systemSymbol: string;
-	waypointSymbol: string;
-};
-
-export type Registration = {
-	factionSymbol: string;
-	name: string;
-	role: string;
-};
-
-export type Route = {
-	arrival: Date;
-	departureTime: Date;
-	destination: RouteEndpoint;
-	origin: RouteEndpoint;
-};
-
-export type RouteEndpoint = {
-	type: string;
-	symbol: string;
-	systemSymbol: string;
-	x: number;
-	y: number;
-};
-
-export type Ship = {
-	cargo: Cargo;
-	cooldown: Cooldown;
-	// crew
-	// engine
-	// frame
-	fuel: Fuel;
-	// modules
-	// mounts
-	nav: Nav;
-	// reactor
-	registration: Registration;
-	symbol: string;
-};
diff --git a/nodejs/model/system.ts b/nodejs/model/system.ts
deleted file mode 100644
index b90560f..0000000
--- a/nodejs/model/system.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { CommonThing } from 'common.ts';
-
-export type Chart = {
-	waypointSymbol: string;
-	submittedBy: string;
-	submittedOn: Date;
-};
-
-export type System = {
-	symbol: string;
-	sectorSymbol: string;
-	type: string;
-	x: number;
-	y: number;
-	waypoints: Array<Waypoint>;
-};
-
-export type Waypoint = {
-	chart: Chart;
-	factions: Array<{symbol: string;}>;
-	isUnderConstruction: boolean;
-	modifiers: Array<CommonThing>;
-	orbitals: Array<{symbol: string;}>;
-	orbits: string;
-	symbol: string;
-	traits: Array<CommonThing>;
-	type: string;
-	x: number;
-	y: number;
-};