Problems I am facing while embedding a private notebook into internal dashboard

Hi!
I am trying to embed some of my Observable notebooks into an internal dashboard I am building. I am using the Observable teams feature and the notebooks that I’m trying to embed are shared across the team. The data is coming from the API that we built and I am using React as a frontend framework for embedding these notebook. I am facing few issues while doing this and seeking your help.

  1. I am not able to install the notebooks as a NPM module

This is how I’m trying to install the notebook: npm i https://api.observablehq.com/d/cc500855cdb11a74@48.tgz?v=3&api_key=xxxxxxxx but apparently NPM does not accept the api_key argument in the command. the second approach I tried for this is to wrap the URL in the double quotes like this npm i "https://api.observablehq.com/d/cc500855cdb11a74@48.tgz?v=3&api_key=xxxxxxxx" now the command works but it is not able to find the module, it gives me this error: npm ERR! 404 Not Found it works if the notebook is public, but in my use case I cannot make the notebook public, it has to be private because of the sensitive data.

  1. Secret() function does not work for the embedded notebook

So for now I have downloaded the tar ball file of the notebook and then installed it as NPM module, but now the Secret() function is not working, it is giving me error: viewof SpeedChart = RuntimeError: Secret is not defined and this is how I am using the Secret() function in my notebook: https://api.mydomain.com/dataapi/api/rest/someapiname/fetch?userid=${Secret( "dev_user" )}&apikey=${Secret("api_key")}

1 Like

taking a look…

1 Like

sorry ive been busy! i just tried a private notebook with an api key and it installed fine; tried embedding a cell in a react app and it worked. hmm

i was using yarn instead of npm, i wonder if that makes a difference

yarn add "https://api.observablehq.com/d/ec8...@6.tgz?v=3&api_key=f98..."

as for secrets, yeah, i think you’ll have to override that in the module; lemme see if i can make a demo of that for you tomorrow, it’d be good for our examples repo

installing via NPM giving me 404 npm ERR! code E404 npm ERR! 404 Not Found - GET https://api.observablehq.com/d/xxxxxxxxxxx@813.tgz?v=3&api_key=.xxxxxxxxxxxx

whereas, if I paste the same URL in the browser it downloads the .tgz file which is strange means the URL should also work with npm install but it is not working

So sorry for the delay! Hm, NPM works for me as well as Yarn. I ran:

npm i "https://api.observablehq.com/d/ec8...@6.tgz?v=3&api_key=f98..."

Could you try opening the same URL in an incognito window where you’re not logged into Observable? I wonder if your API key isn’t working and you can’t tell because when you paste it in the browser you’re already authenticated.

Also, what version of npm are you on? Run npm -v; I see 6.14.14. Run node -v; I see v14.17.5. I wouldn’t expect that to matter, but it’s all I can think to check right now…


As for the Secrets question, take a look at this example, especially index.html. (That’s on a not-yet-merged branch of our repository of embedding examples.)

Here’s the important part of index.html, where we initialize the runtime with a version of the Library where we’ve overridden Secret with our own custom function. Here it’s just hardcoded in index.html, where any visitor to the page could just look at the source and steal your Secret; this is not secure!!

I trust you understand this, but for anyone who comes across this thread, it’s up to you to choose a suitably secure implementation for whatever you’re trying to do with your Secret. Like maybe this is on a private intranet page, or running server-side. Don’t publish a webpage that uses a sensitive Secret in JavaScript that will be sent to untrusted viewers’ computers.

import define from "./index.js";
import {Runtime, Library, Inspector} from "./runtime.js";

// This is an *insecure* example of how to use your own implementation of the
// built-in Secret function by getting values from a hardcoded Map.
const secrets = new Map([["MY_SECRET_KEY", "$w0rdf1sh"]]);
const Secret = () => key => secrets.get(key);

// When we initialize the runtime, we pass it a standard Library object, with
// `Secret` overridden with our custom implementation. Now, when a cell calls
// Secret("MY_SECRET_KEY"), it will return "$w0rdf1sh".
const runtime = new Runtime(Object.assign(new Library, {Secret}));

const main = runtime.module(define, Inspector.into(document.body));
1 Like

Thanks for the solution @tophtucker , I really appreciate it. I am using Gatsby for the frontend so maybe I can use their env variables to provide the secret instead of passing it directly in the index.html I will update you with my solution.

Ooh I’ve been meaning to add a Gatsby example to that repo of examples; it seems to be a popular destination for embedding notebooks but I’ve never used it. Would that have been helpful to you?

1 Like

That would be really helpful!

Hi @tophtucker, I tried to implement your solution in Gatsby.JS but I’m getting this error:

RuntimeError: i is not iterable

Can you please tell if I am doing something wrong. Here’s the code:

const IndexPage = () => {
  const ref = useRef();
  useEffect(() => {
    const secrets = new Map([
      ["MY_SECRET_KEY", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"],
    ]);
    const Secret = () => (key) => secrets.get(key);
    const runtime = new Runtime(Object.assign(new Library(), { Secret }));
    runtime.module(notebook, Inspector.into(ref.current));
    return () => runtime.dispose();
  }, []);
return (

    <main>

      <div className="notebook-wrapper">

        <div ref={ref} />

      </div>

    </main>

  );

};

Thanks!