How to use it

Two steps to knowing where you stand in a goal.

1 Ask what is running.

what community goals are on Ask This one needs the internet. It is off until you turn web access on. Your own contribution comes out of your journal, not the network.

2 Read the tier and your own share.

Alliance Rescue Effort TIER 4 of 8 You have handed in 1,240 tonnes. Your figure is read from your own journal, so it is right even when the site is slow.

! The one that stops people.

It needs the internet, and that is off by default. With web access off there is nothing to report. The privacy page says exactly what leaves.
Why it works this way

What community goals are running, what tier they have reached, and how you are doing in them.

1 Your journal already knows most of this.

YOUR JOURNAL the whole board: tier, contributors, handed in, and your own share WHAT A KEY BUYS goals running somewhere you have not been The event is written off the noticeboard at a station, so it reports where you are. Which is narrower than “everything you have not joined” — your journal covers that on its own.

The key is the switch: with none stored, nothing is requested and nothing leaves this machine. What goes to Inara is your key and nothing else — not your Commander name, not your Frontier id, not where you are.

2 The trap: the board is a snapshot, not a list of live goals.

a board reported on 21 January… …for a goal that ended on the 17th, still carrying “IsComplete: true” It fires every time you dock, so a stale entry is the common case, not the edge one. So every goal is checked against the clock before it is listed. The deadline sits on the second line of every entry rather than buried among the figures.

Announcing a finished goal as something you can still fly for is a wrong answer that reads exactly like the feature working. Expired ones are hidden unless you ask — which is how you find out what a goal paid you.

3 Two sources, never blended.

FROM YOUR JOURNAL and what you handed in FROM INARA and never your standing A duplicate is visible. A wrong merge is silent. A listing entry carries no id, so the only shared field is the name — the field two sources spell differently.

Inara knows what the world handed in; your journal knows what you handed in. And if the listing fails you do not lose the journal half — the goals you have seen are reported as usual, with the reason for the missing half added at the end.

The details

What community goals are running, what tier they have reached, and how you are doing in them.

“what community goals are running” “how am I doing in the community goal” “what tier is the community goal at”

Most of this comes off your own journal and needs nothing. The one thing it cannot do without a key is see a goal running somewhere you have not been.

Your journal already knows more than you would expect

Elite writes a CommunityGoal event carrying the whole board — the goal, where it is flown, the tier it has reached, how many Commanders are on it, how much has been handed in, your own contribution, your percentile band and whether you are in the top rank. All of that is on disk already.

It is written off the noticeboard at a station, though, so it reports what is on offer where you happen to be. Across thirteen months of one Commander’s play that came to ten goals — and 952 of the 16,999 board entries in that corpus record a contribution of zero, which is the game telling you about goals they merely docked near and never joined.

So the line an outside source buys is everywhere you have not been, which is wider than “everything you have not joined”. Your journal covers the second one on its own.

The trap: the board is a snapshot, not a list of live goals

The same corpus holds a board reported on 21 January for a goal that ended on the 17th, carrying IsComplete: true. Four days stale — and because the event fires every time you dock, a stale entry is the common case rather than the edge one.

Every goal is therefore checked against the clock before it is listed, and an expired one says so in the same breath as its name:

2 community goals from your journal:

Alliance Research Initiative — Trade
  Neville Horizons, Kaushpoos — 3 days left
  Tier 1 of 5, 101 contributors, 10,062 delivered.
  You: you have contributed 562, top 50%, the band pays 200,000 cr, signed up.
  Reported 2 hours ago.

Operation Andronicus
  The Oracle, Pleiades Sector IR-W d1-55 — ended 4 days ago
  Tier 4, 408 contributors, 2,838,230,000 delivered, met.
  You: not signed up as far as I know.
  Reported 4 days ago.

Announcing a finished goal as something you can still fly for is a wrong answer that reads exactly like the feature working, which is why the deadline is on the second line of every entry rather than buried in the figures.

Expired goals are hidden unless you ask for them — include_finished — because an expired goal cannot be contributed to. Asking for them is how you find out what a goal paid you.

Inara API key

The one setting, and there is no separate on/off switch: the key is the switch. With no key stored, nothing is requested and nothing leaves this machine, and the answer says plainly that it is only what your journal has seen. Clearing the key is how you turn it off.

Get one from your Inara profile, under API keys. It is stored encrypted for this Windows account and is write-only — d47 will never show it back to you.

What goes to inara.cz is your key and nothing else. Not your Commander name, not your Frontier ID, not where you are, and nothing from your journal — the request is a read of a public board, so it says nothing about anybody. The Privacy section computes that same statement rather than repeating it by hand, so it cannot go stale.

d47 does not use a shared application key, though Inara issues them for read-only requests like this one. d47 ships as a public binary with its source beside it, so a key baked into it would be a published key, and a published key gets abused until it is revoked for everybody.

Two sources, never blended

Goals from Inara are listed separately, under a line saying so:

1 more reported by Inara, which your journal has not seen. Nothing here says anything about your
own contribution:

Rescue Operation in the Pleiades
  The Oracle, Pleiades Sector IR-W d1-55 — 2 days left
  Deliver Occupied Escape Pods, Damaged Escape Pods, Black Boxes and Personal Effects
  Tier 6, 2,038 contributors, 40,001 delivered, met.
  Inara last updated this 9 hours ago.

A listing entry carries no CGID, so the only field the two sources could be matched on is the goal’s name — which is exactly the field two sources spell differently. So they are merged only when the names match exactly, and anything else is allowed to appear twice. A duplicate is visible; a wrong merge is silent.

Your standing never comes from Inara. It knows what the world handed in; your journal knows what you handed in.

When Inara cannot answer

The listing failing does not lose you the journal half. The goals you have seen are reported as usual and the reason for the missing half is added at the end:

Inara rejected the request. Check the API key.

An HTTP 200 from that site is not a success — a bad key, a malformed request and “nothing found” all arrive as 200 with a status code inside the body. Reading the transport code as the answer would report a rejected key as an empty board, which is the one wrong answer here that looks exactly like a right one.

One saved question, run again and again (#296). It is the INARA commodity search a Commander flying a supply goal was typing by hand before every run: buy Palladium, from the goal’s own system, nearest first, within 250 light years, prices under eight hours old, a large pad, a station within 50,000 light seconds of the star, at least 10,000 in stock, no surface stations, no carriers. Every one of those is now a knob on the galaxy search’s find_nearest_station, and this is the one place they are set together.

Two ways to run it, one code path:

  • By voice \u2014 “community goal search” or “CG search”. The phrase is matched whole and first by the keyword router and pointed at the galaxy tool with the arguments already filled in, so it costs no tool-surface bytes and never goes through a model to be reinterpreted.
  • From the Routing tab \u2014 the Community Goal page, beside Plan, Progress, Course and Market, has the commodity in a box and a Run button. The commodity is the one field that moves; the rest is written on the page as the fixed shape of the search.

Saying “refresh” while that page is showing runs it again. It is the first refresh command in d47, and it is kept to that page so the bare word cannot be claimed by this forever.

Where it measures from

The goal’s system, not the ship’s (#331). A supply goal is buy near where you sell, so the useful question is “the best supplier for this run” — one answer that holds for the life of the goal. Measured from the ship it changed with every jump and, once you were standing at a supplier, collapsed to “where you already are”.

The origin is the SystemName of a live goal on your board: the one you are actually running — a contribution or a sign-up is the evidence — and otherwise the most recently reported. A goal whose Expiry has passed is never the origin. With no live goal there is nothing else to measure from, so it falls back to wherever the ship is.

Both the page and the spoken answer say which it was, because “nearest” reads the same whichever place it was asked from:

Nearest to Ega, the goal's system, for buying Palladium: Coleman Relay (Enayex), 11 ly. System is
on your clipboard.

and the heading above the ranking reads “Palladium near Ega, the goal’s system, nearest first”.

To ask it from the ship on purpose — what is close enough to reach right now — say “CG search from here” (or “community goal search from here”, and “refresh from here” while the page is up). That is not a setting: which of the two questions you want is a property of the sentence, not of the installation. The clipboard and “set a course” keep working from wherever the ship is either way, since plotting is the ship’s business — but the distance column is measured from whatever the search was asked from, the same number that ranks the list, and the heading above the table names that system.

With more than one live goal on the board — Elite runs several at once, and the board merges rather than replaces — the goal you are running wins, then the one whose title names the commodity you are searching for, and only then the most recently reported. The title is a tie-break rather than a requirement: the CommunityGoal event carries no commodity field and plenty of goals never name one in their title.

What comes back

The spoken answer is one sentence naming the nearest station that passes every filter, and the rest is left to the page: the price, the stock, the distance from the star and the quote’s age are all in the table, and the ear could not act on them. The page draws the full ranked list: station, system, pad, distance from the star, distance, supply, price, when the quote was reported.

The ledger

What the goal’s commodity has made or lost, net of what the cargo cost \u2014 a Palladium bought at 48,200 and sold at 51,000 shows the 2,800 and not the 51,000. The cost is Elite’s own AvgPricePaid on each sale, which the game tracks across sessions and ships; when Elite writes zero \u2014 cargo that was never bought \u2014 it falls back to the average of your own MarketBuy events for the commodity, and failing that the sale is gross, which is the accurate figure for a mined load.

Three stretches, all on the Community Goal page; only the first is spoken automatically, and the other two are answers to a question (#332, #340, #342):

  • This session \u2014 the running total rather than the sale, said automatically after every sale of the commodity: “That’s 2 million up this session.” You can see the sale’s own figure on your screen at that moment; what you cannot see is where it leaves you. The Community Goal sales row on the Callouts page switches the sentence off, and nothing is said while d47 is catching up on a journal it did not watch being written.
  • Today \u2014 asked for, never announced: “how have I done today”.
  • This week \u2014 asked for, never announced: “how have I done this week”, or “how have I done since the last maintenance window” (also “since maintenance”, “since the last maintenance”, “since the tick”). The week is the Elite week, turning at the week boundary, Thursday 07:00 UTC by default \u2014 where the Powerplay cycle, the BGS tick and the weekly server maintenance all sit. The exact minute does not matter to a ledger: nothing can be sold while the servers are down, so a window opening at the hour holds the same sales as one opening at “live again”. The answer names the boundary it used, so “this week” says what it means without a trip to settings: “Palladium: 2.1 billion up this week, since Thursday’s maintenance \u2014 7 sales, 8,400 tonnes\u2026” There is no fourth, goal-named stretch (#340): “how have I done on the community goal” and “how am I doing on the community goal” answer “this week”, the same as asking for the week outright \u2014 that is the answer to how a run is going while a goal is live.

The day and the week both reach across sessions because the journal files Elite keeps are read at startup \u2014 the files that cover the last ten days \u2014 and the live journal after that. Nothing is written under data\: the journals are the record, and a sale the startup read already counted is recognised rather than counted twice when the live journal replays it.

Palladium: 3 million up this week \u2014 3 sales, 300 tonnes, 15,000,000 cr in against
12,000,000 cr the cargo cost.

The week boundary

Which day, UTC, and which hour “this week” turns over on \u2014 two advanced rows, Thursday and 07:00 by default. Frontier has moved this boundary before, so it is a setting rather than a constant; it is a fact about their schedule and not about d47.

Tools

get_community_goals

{"type":"object","properties":{"include_finished":{"type":"boolean","description":"Also list goals that have already expired, with what they paid out. Default false \u2014 an expired goal cannot be contributed to."},"name":{"type":"string","description":"Only goals whose title contains this. Leave out for all of them."}},"required":[],"additionalProperties":false}

get_community_goal_earnings

What the Community Goal commodity has made or lost, net of cost, over one of three stretches. The nine questions above reach it through the keyword router without a model; the model can call it for anything phrased differently.

{"type":"object","properties":{"range":{"type":"string","description":"Which stretch. Default session.","enum":["session","today","week"]}},"required":[],"additionalProperties":false}

Notes for anyone reading the code

The board is merged by CGID, never replaced. CurrentGoals looks like a complete board per event, which would argue for replacing it — but it is the board at one station, and no station in the corpus ran more than one goal at a time, so the two-stations case is untested. Replacing on an untested assumption loses a goal you are actually running; merging keeps one that has ended, which the expiry check already has to handle because the snapshot goes stale regardless. Only one of those two failures is silent.

CommunityGoalJoin, CommunityGoalDiscard and CommunityGoalReward carry only CGID, Name and System — so signing up and being paid are merged onto the board entry rather than read from it, and they survive the next board event. Taking a fresh read’s defaults would un-join a goal every time you docked. One arriving for a goal no board has reported still counts: a Commander who joined a goal before d47 was watching is not a Commander who has not joined it.

TierReached is written as the string "Tier 3" and only once the first success tier is met, so “no tier reached yet” is a fact rather than a gap. Inara’s tierMax is 0 when it does not know — the journals carry no maximum tier, so an entry built from a journal upload has zero there, and reading it literally announces a goal whose top tier is zero.

The goal’s GalNet copy — several hundred words per goal, in goalDescriptionText — is dropped at the seam rather than trimmed. It is the largest piece of untrusted third-party text d47 could put in front of a model, and nobody asked for it. The one-line objective comes through, capped.

The wire shape follows Inara’s API documentation as read on 2026-08-15: one endpoint, an envelope of a header and an events array, and per-event status codes underneath the HTTP one. getCommunityGoalsRecent takes no properties.

Findings behind all of this are in docs/spikes/community-goals.md.