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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions faucet_service/faucet_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -1595,7 +1595,7 @@ def get_template_vars(config: Dict) -> Dict:
submitBtn.disabled = true;
submitBtn.textContent = 'Processing...';
result.className = 'result';
result.innerHTML = '';
result.textContent = '';

const wallet = walletInput.value.trim();

Expand All @@ -1611,7 +1611,7 @@ def get_template_vars(config: Dict) -> Dict:
result.className = 'result show ' + (data.ok ? 'success' : 'error');

if (data.ok) {
result.innerHTML = '';
result.textContent = '';
const strong = document.createElement('strong');
strong.textContent = '✅ Success!';
result.appendChild(strong);
Expand All @@ -1627,7 +1627,7 @@ def get_template_vars(config: Dict) -> Dict:
walletInput.value = '';
loadStats();
} else {
result.innerHTML = '';
result.textContent = '';
const strong = document.createElement('strong');
strong.textContent = `❌ ${data.error}`;
result.appendChild(strong);
Expand All @@ -1640,7 +1640,7 @@ def get_template_vars(config: Dict) -> Dict:
}
} catch (err) {
result.className = 'result show error';
result.innerHTML = '';
result.textContent = '';
const strong = document.createElement('strong');
strong.textContent = '❌ Error: ';
result.appendChild(strong);
Expand Down
8 changes: 5 additions & 3 deletions node/beacon_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1368,9 +1368,11 @@ def chat():

@beacon_api.route('/relay/discover', methods=['GET'])
def relay_discover():
"""Discover relay agents (for 3D visualization)."""
# In production, query the relay registry
# For demo, return empty array
"""Discover relay agents (for 3D visualization).

.. deprecated::
Use /beacon/atlas instead. This endpoint returns an empty array.
"""
return jsonify([])


Expand Down
2 changes: 1 addition & 1 deletion site/beacon/advertise.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const LISTING_TIERS = [
benefits: [
'Your agent appears as a permanent node on the 3D Atlas',
'Custom city placement based on your agent capabilities',
'Listed in /relay/discover API for cross-agent collaboration',
'Listed in /beacon/atlas for cross-agent collaboration',
'Reputation score tracking and bounty eligibility',
'Featured in "Integrated Partners" section',
'Access to Beacon contract and mayday systems',
Expand Down
5 changes: 3 additions & 2 deletions site/beacon/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -584,9 +584,10 @@ export async function fetchAllAgents(apiBase) {

// --- 2. Beacon relay agents ---
try {
const resp = await fetch(`${apiBase}/relay/discover`);
const resp = await fetch(`${apiBase}/beacon/atlas`);
if (resp.ok) {
const relays = await resp.json();
const atlasData = await resp.json();
const relays = atlasData.agents || atlasData;
for (const ra of relays) {
const canonicalId = resolveFromAliasMap(aliasMap, ra.name, ra.model_id, ra.agent_id);
if (canonicalId && agentMap.has(canonicalId)) {
Expand Down
164 changes: 161 additions & 3 deletions tests/test_vintage_ai_rustchain_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ def test_get_miners_accepts_envelope_payloads(monkeypatch):

monkeypatch.setattr(
client,
"_get",
lambda endpoint: {
"_get_public",
lambda endpoint, params=None: {
"items": [
{"miner": "alice", "hardware_type": "PowerPC G4"},
{"miner": "bob", "hardware_type": "x86-64"},
Expand All @@ -40,7 +40,7 @@ def test_get_miners_returns_empty_list_for_unexpected_payload(monkeypatch):
module = load_client_module()
client = module.RustChainClient(base_url="https://node.example")

monkeypatch.setattr(client, "_get", lambda endpoint: {"pagination": {"total": 0}})
monkeypatch.setattr(client, "_get_public", lambda endpoint, params=None: {"pagination": {"total": 0}})

assert client.get_miners() == []

Expand Down Expand Up @@ -138,3 +138,161 @@ def read(self):
return body

return FakeResp()


# --- Issue #6624: _request_public must NOT include admin key headers ---


def test_request_public_uses_public_headers(monkeypatch):
"""_request_public must use _get_public_headers (no admin key)."""
module = load_client_module()
client = module.RustChainClient(
base_url="https://node.example", admin_key="secret-admin-key-123"
)

captured_headers = {}

class FakeResp:
def __enter__(self):
return self
def __exit__(self, *args):
return False
def read(self):
return b'{"ok": true}'

def fake_urlopen(req, **kwargs):
captured_headers.update(req.headers)
return FakeResp()

monkeypatch.setattr("urllib.request.urlopen", fake_urlopen)

result = client._request_public("GET", "/health")
assert result == {"ok": True}
assert "X-Admin-Key" not in captured_headers, (
f"_request_public must not send admin key; got headers: {captured_headers}"
)
assert captured_headers.get("Accept") == "application/json"


def test_request_with_admin_key_sends_header(monkeypatch):
"""_request (authenticated) must include X-Admin-Key when configured."""
module = load_client_module()
client = module.RustChainClient(
base_url="https://node.example", admin_key="secret-admin-key-123"
)

captured_headers = {}

class FakeResp:
def __enter__(self):
return self
def __exit__(self, *args):
return False
def read(self):
return b'{"ok": true}'

def fake_urlopen(req, **kwargs):
captured_headers.update(req.headers)
return FakeResp()

monkeypatch.setattr("urllib.request.urlopen", fake_urlopen)

result = client._request("POST", "/api/submit", data={"key": "val"})
assert result == {"ok": True}
headers_lower = {k.lower(): v for k, v in captured_headers.items()}
assert headers_lower.get("x-admin-key") == "secret-admin-key-123"


def test_read_methods_use_public_no_admin_key(monkeypatch):
"""Read methods (health, get_epoch, get_miners, etc.) must not send admin key."""
module = load_client_module()
client = module.RustChainClient(
base_url="https://node.example", admin_key="secret-admin-key-123"
)

captured_headers = {}

class FakeResp:
def __enter__(self):
return self
def __exit__(self, *args):
return False
def read(self):
return b'{"result": "ok"}'

def fake_urlopen(req, **kwargs):
captured_headers.clear()
captured_headers.update(req.headers)
return FakeResp()

monkeypatch.setattr("urllib.request.urlopen", fake_urlopen)

read_endpoints = [
lambda: client.health(),
lambda: client.get_epoch(),
lambda: client.get_wallet_balance("miner_123"),
lambda: client.get_wallet_history("miner_123"),
lambda: client.get_stats(),
lambda: client.get_hall_of_fame(),
lambda: client.get_miner_eligibility("miner_123"),
]

for call in read_endpoints:
call()
assert "X-Admin-Key" not in captured_headers, (
f"Read method must not send admin key; got: {captured_headers}"
)


def test_admin_key_not_set_no_header_sent(monkeypatch):
"""When admin_key is None, no X-Admin-Key header is sent even on write requests."""
module = load_client_module()
client = module.RustChainClient(base_url="https://node.example")

captured_headers = {}

class FakeResp:
def __enter__(self):
return self
def __exit__(self, *args):
return False
def read(self):
return b'{"ok": true}'

def fake_urlopen(req, **kwargs):
captured_headers.update(req.headers)
return FakeResp()

monkeypatch.setattr("urllib.request.urlopen", fake_urlopen)

client._request("POST", "/api/submit", data={"key": "val"})
assert "X-Admin-Key" not in captured_headers


def test_get_public_headers_never_includes_admin_key():
"""_get_public_headers() must never include admin key regardless of config."""
module = load_client_module()
client = module.RustChainClient(
base_url="https://node.example", admin_key="super-secret"
)
headers = client._get_public_headers()
assert "X-Admin-Key" not in headers
assert "Accept" in headers


def test_get_headers_includes_admin_key_when_set():
"""_get_headers() includes admin key when configured."""
module = load_client_module()
client = module.RustChainClient(
base_url="https://node.example", admin_key="my-admin-key"
)
headers = client._get_headers()
assert headers["X-Admin-Key"] == "my-admin-key"


def test_get_headers_no_admin_key_when_unset():
"""_get_headers() omits admin key when not configured."""
module = load_client_module()
client = module.RustChainClient(base_url="https://node.example")
headers = client._get_headers()
assert "X-Admin-Key" not in headers
12 changes: 8 additions & 4 deletions tools/bcos-badge-generator/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -732,14 +732,14 @@ <h1>BCOS Badge Generator</h1>
img.alt = `BCOS Badge for ${certId}`;

img.onload = () => {
previewArea.innerHTML = '';
previewArea.replaceChildren();
previewArea.appendChild(img);
resolve();
};

img.onerror = () => {
// For demo purposes, show a placeholder if endpoint is unavailable
previewArea.innerHTML = '';
previewArea.replaceChildren();
const container = document.createElement('div');
container.style.textAlign = 'left';

Expand Down Expand Up @@ -808,7 +808,11 @@ <h1>BCOS Badge Generator</h1>
function setLoading(loading) {
if (loading) {
generateBtn.disabled = true;
generateBtn.innerHTML = '<span class="spinner"></span>Generating...';
generateBtn.replaceChildren();
const spinner = document.createElement('span');
spinner.className = 'spinner';
generateBtn.appendChild(spinner);
generateBtn.appendChild(document.createTextNode('Generating...'));
} else {
generateBtn.disabled = false;
generateBtn.textContent = 'Generate Badge';
Expand All @@ -821,7 +825,7 @@ <h1>BCOS Badge Generator</h1>
currentStyle = 'flat';
styleBtns.forEach(b => b.classList.remove('active'));
styleBtns[0].classList.add('active');
previewArea.innerHTML = '';
previewArea.replaceChildren();
const span = document.createElement('span');
span.className = 'preview-placeholder';
span.textContent = 'Enter a Certificate ID and click "Generate Preview" to see your badge';
Expand Down
Loading
Loading