How to use it
Two steps to knowing where you stand in a goal.
1 Ask what is running.
2 Read the tier and your own share.
! The one that stops people.
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.
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.
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.
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.
The Community Goal search
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.